2013-05-23 5 views
12

Mi interessa sapere su come verificare la funzionalità Attore Akka, deridendo alcuni metodi (implementazione del metodo sostituto reale dell'oggetto/attore da deriso uno) in attore.Come verificare la funzionalità Attore Akka deridendo uno o più metodi in esso

Io uso akka.testkit.TestActorRef;

Inoltre: ho provato a utilizzare SpyingProducer ma non è chiaro come utilizzarlo. (come me se avessi creato un attore all'interno della sua implementazione sarebbe lo stesso che ho ora). Il risultato della ricerca di Google su questo non è molto verbose.

Io uso powemockito e java. Ma non importa. Sarei interessato a sapere how to do it in principlecon qualsiasi linguaggio con qualsiasi quadro

(quindi se non si sa come potere/Mockito funziona limita a fornire il codice .. (per favore) o idea completa di come si farebbe . farlo con gli strumenti che conoscete)

Quindi, supponiamo di avere un attore di prova:

package example.formock; 

import akka.actor.UntypedActor; 

public class ToBeTestedActor extends UntypedActor { 

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

     if (message instanceof String) { 
      getSender().tell(getHelloMessage((String) message), getSelf()); 
     } 

    } 

    String getHelloMessage(String initMessage) { // this was created for test purposes (for testing mocking/spy capabilities). Look at the test 
     return "Hello, " + initMessage; 
    } 

} 

E nel nostro test vogliamo sostituire 01.231.126,963 milarestituire qualcos'altro.

Questo è il mio tentativo:

package example.formock; 

import akka.testkit.TestActorRef; 
... 

@RunWith(PowerMockRunner.class) 
@PrepareForTest(ToBeTestedActor.class) 
public class ToBeTestedActorTest { 

    static final Timeout timeout = new Timeout(Duration.create(5, "seconds")); 

    @Test 
    public void getHelloMessage() { 

     final ActorSystem system = ActorSystem.create("system"); 

     // given 
     final TestActorRef<ToBeTestedActor> actorRef = TestActorRef.create(
       system, 
       Props.create(ToBeTestedActor.class), 
       "toBeTestedActor"); 

     // First try: 
     ToBeTestedActor actorSpy = PowerMockito.spy(actorRef.underlyingActor()); 
     // change functionality 
     PowerMockito.when(actorSpy.getHelloMessage (anyString())).thenReturn("nothing"); // <- expecting result 


     try { 

      // when 
      Future<Object> future = Patterns.ask(actorRef, "Bob", timeout); 
      // then 
      assertTrue(future.isCompleted()); 

      // when 
      String resultMessage = (String) Await.result(future, Duration.Zero()); 
      // then 
      assertEquals("nothing", resultMessage); // FAIL HERE 

     } catch (Exception e) { 
      fail("ops"); 
     } 
    } 
} 

Risultato:

org.junit.ComparisonFailure: 
Expected :nothing 
Actual :Hello, Bob 

risposta

1

, quindi sono probabilmente non capire la domanda, ma che probabilmente non vogliono prendere in giro un attore come lo scopo di deridere è sostituire qualcosa come un dao con una copia di prova che ha aspettative di invocazione - l'attore non si adatta veramente al conto in quanto è qualcosa che estendi piuttosto che una dipendenza - il mocking si applica solo alle dipendenze reali.

TestActorRef consente in particolare di accedere all'attore sottostante: in circostanze normali è possibile inviare solo messaggi a un attore e non richiamare direttamente nulla su di esso. TestActoRef rimuove questa limitazione permettendoti di accedere alla vera vera estensione di Actor invece che solo ActorRef che puoi solo! o ? contro (inviare o chiedere).

Sono uno scalone, quindi l'intuizione è sperabilmente agnostica. Non conosco la java api in modo specifico ma non dovrebbe avere importanza.

La mia raccomandazione è di ottenere l'oggetto attore reale tramite l'attore ref e solo testare il metodo o capire in qualche modo di ottenere la copertura di test tramite messaggi reali.

+0

Come ti testare in scala? mocking qui significa - prendere in giro un metodo. cioè sostituire un metodo su un oggetto reale (è per questo che usiamo spy() - essere in grado di mettere la nostra implementazione al metodo - lasciare che il metodo sappia cosa tornare per parametri particolari del metodo e non usare il corpo reale del metodo in onReceive () ma deriso uno). "TestActorRef consente in particolare di accedere all'attore sottostante" - ecco perché voglio utilizzare questo attore, ma non i juts inviare messaggi. Mocking and Spying è una pratica generica per Java, e in questo caso non funziona, anche se abbiamo accesso all'oggettivo obj – ses

+0

E perché scala ragazzi così orgogliosi che conoscono scala ma non conoscono java? (senza offesa) :) – ses

+0

Conosco java meglio di quanto conosca scala - solo non l'akka java api! – JasonG

10

Akka ha una classe AutoPilot che è fondamentalmente una simulazione generale per gli attori, con la possibilità di rispondere ai messaggi e affermare che i messaggi sono stati inviati. http://doc.akka.io/docs/akka/snapshot/java/testing.html

Ecco l'esempio di Java per quella pagina. Crea una sonda, imposta un pilota automatico in grado di rispondere ai messaggi e prendi uno ActorRef da cui puoi sostituire il tuo vero attore.

new JavaTestKit(system) {{ 
    final JavaTestKit probe = new JavaTestKit(system); 
    // install auto-pilot 
    probe.setAutoPilot(new TestActor.AutoPilot() { 
    public AutoPilot run(ActorRef sender, Object msg) { 
     sender.tell(msg, ActorRef.noSender()); 
     return noAutoPilot(); 
    } 
    }); 
    // first one is replied to directly ... 
    probe.getRef().tell("hello", getRef()); 
    expectMsgEquals("hello"); 
    // ... but then the auto-pilot switched itself off 
    probe.getRef().tell("world", getRef()); 
    expectNoMsg(); 
}}; 
+0

Thx per l'aggiornamento. Ho intenzione di giocare con quest'ultimo. vedrà caldo si adatta alle mie esigenze/capire come dovrebbe essere. – ses

2

non ho alcuna esperienza nell'utilizzo di Akka con Java, ma credo che la soluzione per questo io uso a Scala può valere anche per Java. Non c'è affatto bisogno di prendere in giro qualcosa. In Java il mocking a volte è utile per i test, ma la mia personale esperienza/opinione è che ogni volta che si ha bisogno di PowerMock si sta facendo qualcosa di sbagliato.

Ecco come provo a testare utilizzando Akka:

In Scala io uso un tratto (interfaccia aka) in cui sono definiti i metodi di attore.

trait ToBeTested { 
    def getHelloMessage(msg: String, replyTarget: ActorRef): String = 
     replyTarget ! s"Hello $msg" 
} 

In questo modo, questa funzionalità può essere sottoposta a test dell'unità molto semplice. Per il vero attore cerco di limitarmi a implementare solo il metodo di ricezione.

class ToBeTestedActor extends Actor with ToBeTested { 
    def receive: Receive = { 
    case msg: String => getHelloMessage(msg, sender()) 
    } 
} 

Quindi, durante il test dell'attore, è possibile eseguire l'override dell'implementazione getHelloMessage per fare ciò che si desidera.

class ToBeTestedActorTest extends TestKit(ActorSystem("toBeTested") with .... { 
    trait MyToBeTested extends ToBeTested { 
    // do something predictable for testing or defer to a TestProbe which you can 
    // either define globally in the test class or provide one in a constructor. 
    override def getHelloMessage(msg: String, replyTarget: ActorRef): String = ??? 
    } 

    val toBeTestedActor = TestActorRef(Probe(new ToBeTestedActor with MyToBeTested)) 

    // ... (test cases) 
} 

In Java è possibile fare praticamente la stessa cosa. Dal momento che Java 8 è possibile fornire implementazioni di metodo predefinite nelle interfacce, che è possibile eseguire l'override in una sotto-interfaccia per il test. Un altro modo sarebbe quello di sottoclasse l'attore nel test per scavalcare alcuni metodi per fornire un comportamento prevedibile.

// An easy unit testable interface 
public interface ToBeTested { 

    public ActorRef self(); 

    default public void getHelloMessage(String msg, ActorRef replyTarget) { 
    replyTarget.tell(String.format("Hello %s", msg), self()); 
    } 
} 

public class ToBeTestedActor extends UntypedActor implements ToBeTested { 

    // self() already implemented by Actor class 

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

    if (message instanceof String) { 
     getHelloMessage((String)message, getSender()); 
    } 
    } 
} 

public class ToBeTestedActorTest { 

    @Test 
    public void test() throws Exception { 
    ActorSystem system = ActorSystem.create(); 

    TestActorRef<Actor> testActorRef = TestActorRef.create(system, Props.create(TestActor.class)); 

    Future<Object> response = Patterns.ask(testActorRef, "World", 1000); 
    assertThat(response.isCompleted(), is(true)); 
    assertThat(Await.result(response, Duration.Zero()), is("Test")); 
    } 

    // Override interface when using Java 8 
    interface DummyToBeTested extends ToBeTested { 
    @Override 
    default void getHelloMessage(String msg, ActorRef replyTarget) { 
     assertThat(msg, is("World")); 
     replyTarget.tell("Test", self()); 
    } 
    } 

    // extend ToBeTestedActor with dummy interface 
    static class TestActor extends ToBeTestedActor implements DummyToBeTested {} 

    // Or (pre Java 8) extend the ToBeTestedActor directly 
    // static class TestActor extends ToBeTestedActor { 
    //  @Override 
    //  public void getHelloMessage(String msg, ActorRef replyTarget) { 
    //   replyTarget.tell("Test", self()); 
    //  } 
    // } 
} 
0
To mock an actor is easier through the TestActorRef. You can use this code : 

    static ActorSystem system = ActorSystem.create(); 
    static Props propsSome = Props.create(MockedResultActor.class); 

    TestActorRef<MockedResultActor> refMockedResultActor= TestActorRef.create(
        system, propsSome, "testA"); 

    // Mocking an actor class and returning our reference actor 
    PowerMockito.mockStatic(ClassToBeMocked.class); 
    Mockito.when(ClassToBeMocked.getToBeMockedMethod()) 
        .thenReturn(refMockedResultActor); 

Note: ClassToBeMocked--Its a class you want to mock. 
MockedResultActor -- Its a class you want to return after mocking. 
This can be run using JunitTest after implementing basic configuration of mocking in your class. Code given here is specific to akka actor in java only.