2014-09-24 1 views
6

Ho un attore FooActor che viene passato il Props per istanziare diversi BarActor s e inviare BarMessage s ad esso. Il codice funziona, ma sto facendo fatica a scrivere test per questo. La restrizione aggiunta è che posso usare solo il codice Java in questa app, nessun codice Scala.Come verificare che Actor Foo invii messaggi agli attori figli appena creati Bar?

Dopo vari tentativi, questo sembra essere il mio migliore sforzo finora:

@Test 
public void testJavaTestKit() { 

    new JavaTestKit(system) {{ 
     JavaTestKit probe = new JavaTestKit(system); 

     // pretending that the probe is the receiving Bar, by returning it in the Props 
     Props barActorProps = Props.create(BarActor.class, new Creator() { 
      @Override 
      public Object create() { 
       return probe.getRef(); 
      } 
     }); 
     Props props = Props.create(FooActor.class, barActorProps); 
     ActorRef subject = system.actorOf(props); 

     Object msg = // basically irrelevant, will trigger Bar instantiation and message sending 

     subject.tell(msg, probe.getRef()); 

     expectMsgClass(Bar.BarMessage.class); 
     expectNoMsg(); 
    }}; 
} 

Tutto sembra avere un senso per me, ma anche se posso vedere i messaggi inviati a nuova creazione Bar casi, il la prima asserzione fallisce. Che cosa sto facendo di sbagliato?

Aggiornamento:

La cosa che rende questo diverso dall'esempio documentazione di Akka, è che io non voglio passare un attore esistente che riceve il messaggio. Invece, voglio passare lo Props che viene utilizzato per creare invece istanze di attori secondari. Nel test, voglio che il mio probe riceva messaggi a quegli attori appena creati. Questo è il motivo per cui ho aggiunto il costrutto Props.create che dovrebbe restituire sempre lo stesso probe actor. Proprio ora ho visto questo commento nell'API Creator.create:

Questo metodo deve restituire un'istanza diversa ad ogni chiamata.

Quindi questo ovviamente non funzionerà, in quanto è esattamente ciò che voglio. Quindi la mia domanda generale rimane: come posso testare i messaggi inviati agli attori figli appena creati?

+2

È il probe JavaTestKit ActorRef in cui ci si aspetta che un messaggio venga inviato? Se è così, è necessario chiamare probe.expectMsgClass, al momento si sta facendo valere contro il JavaTestKit anonimo nel metodo di test. A meno che il tuo test "soggetto" non risponda con un Bar.BarMessage.class, l'asserzione fallirà sempre – nickebbitt

+2

La soluzione potrebbe essere semplice come cambiare l'asserzione a 'probe.expectMsgClass (Bar.BarMessage.class);' – nickebbitt

+0

Grazie per il tuo risposta.Sembrava davvero promettente, e forse mi ha avvicinato alla soluzione, anche se non funziona ancora in questo momento. –

risposta

4

Stai cercando di "barare" su come gli attori bambini vengono inizializzati (passando probeRef) per avere flessibilità nel loro collaudo, ma il problema è che il anche Creator è generico, utilizzato nel contesto di serie getContext().actorOf(), non può tornare ActorRef come risultato del metodo create. Il contratto deve sempre essere il seguente Creator<T extends Actor>.

prega di dare un'occhiata al ActorCreationTest

Potrei sbagliarmi, perché, non so come il vostro FooActor è implementato, ma se si utilizzerà stadard modello getContext().actorOf() predefinita Akka akka.actor.CreatorConsumer non accetteranno la tua Consumer.

caused by: java.lang.ClassCastException: akka.actor.RepointableActorRef cannot be cast to akka.actor.Actor 
    at akka.actor.CreatorConsumer.produce(Props.scala:335) 
    at akka.actor.Props.newActor(Props.scala:252) 
    at akka.actor.ActorCell.newActor(ActorCell.scala:552) 
    at akka.actor.ActorCell.create(ActorCell.scala:578) 
    ... 9 more 

Invece di tornare ActorRef per sondare si può provare a tornare mittente anonimo Actor?

import akka.actor.*; 
import akka.japi.Creator; 
import akka.testkit.JavaTestKit; 
import org.junit.Test; 

public class ActorTest { 

    @Test 
    public void testJavaTestKit() { 
     ActorSystem system = ActorSystem.create("Acceptor"); 

     new JavaTestKit(system) {{ 
      JavaTestKit probe = new JavaTestKit(system); 

      Creator creator =() -> new UntypedActor() { 
       @Override 
       public void onReceive(Object message) throws Exception { 
        probe.getRef().tell(message, sender()); 
       } 
      }; 


      ActorRef subject = system.actorOf(Props.create(FooActor.class, creator)); 

      subject.tell(new Bar().new BarMessage(), probe.getRef()); 
      probe.expectMsgClass(Bar.BarMessage.class); 


     }}; 
    } 

} 


class BarActor extends UntypedActor { 

    @Override 
    public void onReceive(Object message) throws Exception { 

    } 
} 

class FooActor extends UntypedActor { 

    ActorRef barRef; 

    public static Props props(final Creator creator) { 
     return Props.create(FooActor.class,() -> new FooActor(creator)); 
    } 
    public FooActor(Creator creator) { 
     barRef = getContext().actorOf(Props.create(creator),"bar"); 
    } 

    @Override 
    public void onReceive(Object message) throws Exception { 
     barRef.tell(message,sender()); 
    } 
} 

class Bar { 
    class BarMessage { 

    } 
} 
1

Penso che qualcosa del genere dovrebbe funzionare per il tuo caso.

class ActorSpec extends Specification { 
    @Shared 
    private ActorSystem system 
    @Shared 
    private ActorRef actorRef 
    def setupSpec() { 
     system = ActorSystem.create("test-system",ConfigFactory.load("application.conf")) 
    } 
    def "Verify message received"() { 
    given: 
      JavaTestKit probe = new JavaTestKit(system) 
      final Props props = Props.create(BarActor.class) 
      actorRef = system.actorOf(props) 
     when: 
      actorRef.tell(new BarMessage(), probe.getRef()) 
     then: 
      probe.expectMsgClass(Bar.BarMessage.class); 
    } 
def cleanupSpec() { 
     JavaTestKit.shutdownActorSystem(system) 
     system = null 
    }