Comment tester les fonctionnalités d’Akka Actor en mocking d’une ou de plusieurs méthodes

Je suis intéressé à savoir comment tester les fonctionnalités d’Akka Actor, en mocking de certaines méthodes ( substituer la mise en œuvre de la méthode d’un object réel / acteur par une moquée ) dans Actor.

J’utilise akka.testkit.TestActorRef ;

Aussi: j’ai essayé d’utiliser SpyingProducer mais il n’est pas clair comment l’utiliser. (comme si je créais un acteur dans son implémentation, ce serait le même que j’ai maintenant). Le résultat de la recherche sur Google à ce sujet n’est pas très détaillé .

J’utilise powemockito et java . Mais ça ne fait rien. Je serais intéressé de savoir how to do it in principle avec n’importe quelle langue avec n’importe quel cadre

(donc si vous ne savez pas comment fonctionne power / mockito, donnez simplement votre code .. (s’il vous plait) ou donnez une idée de la façon dont vous le feriez avec vos outils que vous connaissez.)

Alors, disons que nous avons un acteur à tester:

 package example.formock; import akka.actor.UntypedActor; public class ToBeTestedActor extends UntypedActor { @Override public void onReceive(Object message) throws Exception { if (message instanceof Ssortingng) { getSender().tell( getHelloMessage((Ssortingng) message), getSelf()); } } Ssortingng getHelloMessage(Ssortingng initMessage) { // this was created for test purposes (for testing mocking/spy capabilities). Look at the test return "Hello, " + initMessage; } } 

Et dans notre test, nous souhaitons remplacer getHelloMessage() un autre résultat.

C’est mon essai:

 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 actorRef = TestActorRef.create( system, Props.create(ToBeTestedActor.class), "toBeTestedActor"); // First try: ToBeTestedActor actorSpy = PowerMockito.spy(actorRef.underlyingActor()); // change functionality PowerMockito.when(actorSpy.getHelloMessage (anySsortingng())).thenReturn("nothing"); // <- expecting result try { // when Future future = Patterns.ask(actorRef, "Bob", timeout); // then assertTrue(future.isCompleted()); // when Ssortingng resultMessage = (Ssortingng) Await.result(future, Duration.Zero()); // then assertEquals("nothing", resultMessage); // FAIL HERE } catch (Exception e) { fail("ops"); } } } 

Résultat:

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

    Akka a une classe AutoPilot qui est fondamentalement une maquette pour les acteurs, avec la possibilité de répondre aux messages et d’affirmer que les messages ont été envoyés. http://doc.akka.io/docs/akka/snapshot/java/testing.html

    Voici l’exemple de Java pour cette page. Vous créez une sonde, configurez un pilote automatique capable de répondre aux messages et obtenez un ActorRef que vous pouvez remplacer par votre véritable acteur.

     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(); }}; 

    Je n’ai aucune expérience de l’utilisation d’Akka avec Java, mais je suppose que la solution que j’utilise dans Scala peut également s’appliquer à Java. Il n’y a pas du tout besoin de se moquer de rien. En Java, se moquer est parfois utile pour les tests, mais mon expérience / opinion personnelle est que chaque fois que vous avez besoin de PowerMock, vous faites quelque chose de mal.

    Voici comment j’essaie de tester avec Akka:

    En Scala, j’utilise un trait (aka interface) dans lequel les méthodes d’acteur sont définies.

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

    De cette façon, cette fonctionnalité peut être testée à l’unité très facilement. Pour le vrai acteur, j’essaie de coller uniquement à la méthode de réception.

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

    Ensuite, lors du test de l’acteur, vous pouvez redéfinir l’implémentation getHelloMessage pour faire ce que vous voulez.

     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: Ssortingng, replyTarget: ActorRef): Ssortingng = ??? } val toBeTestedActor = TestActorRef(Probe(new ToBeTestedActor with MyToBeTested)) // ... (test cases) } 

    En Java, vous pouvez faire à peu près la même chose. Depuis Java 8, vous pouvez fournir des implémentations de méthodes par défaut dans les interfaces, que vous pouvez remplacer dans une sous-interface pour les tests. Une autre méthode consiste à sous-classer l’acteur dans votre test pour remplacer certaines méthodes afin de fournir un comportement prévisible.

     // An easy unit testable interface public interface ToBeTested { public ActorRef self(); default public void getHelloMessage(Ssortingng msg, ActorRef replyTarget) { replyTarget.tell(Ssortingng.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 Ssortingng) { getHelloMessage((Ssortingng)message, getSender()); } } } public class ToBeTestedActorTest { @Test public void test() throws Exception { ActorSystem system = ActorSystem.create(); TestActorRef testActorRef = TestActorRef.create(system, Props.create(TestActor.class)); Future 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(Ssortingng 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(Ssortingng msg, ActorRef replyTarget) { // replyTarget.tell("Test", self()); // } // } } 

    Donc, je ne comprends probablement pas la question, mais vous ne voulez probablement pas vous moquer d’un acteur, car le but de se moquer est de remplacer un dao par une copie de test qui comporte des attentes d’invocation – l’acteur ne convient pas vraiment. comme c’est quelque chose que vous prolongez plutôt qu’une dépendance – la moquerie ne s’applique vraiment qu’aux dépendances réelles.

    TestActorRef vous donne spécifiquement access à l’acteur sous-jacent – dans la plupart des circonstances normales, vous pouvez uniquement envoyer des messages à un acteur et ne rien invoquer directement. TestActoRef supprime cette limitation en vous permettant d’accéder à votre véritable véritable extension d’Actor au lieu de la seule ActorRef que vous pouvez seulement! ou ? contre (envoyer ou demander).

    Je suis un scala dev, donc la perspicacité est, espérons-le, agnostique. Je ne connais pas spécifiquement l’API Java, mais cela ne devrait pas avoir d’importance.

    Ma recommandation est d’obtenir l’object réel de l’acteur via la référence de l’acteur et de tester la méthode ou de trouver un moyen d’obtenir une couverture de test par le biais de messages réels.

    Se moquer d’un acteur est plus facile grâce à TestActorRef. Vous pouvez utiliser ce code:

     static ActorSystem system = ActorSystem.create(); static Props propsSome = Props.create(MockedResultActor.class); TestActorRef 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); 

    Remarque: ClassToBeMocked – C’est une classe que vous voulez simuler. MockedResultActor – C’est une classe que vous voulez retourner après vous être moqué. Cela peut être exécuté en utilisant JunitTest après avoir implémenté la configuration de base du moquage dans votre classe. Le code donné ici est spécifique à l’acteur akka en java uniquement.