如何通过模拟其中的一个或多个方法来测试Akka Actorfunction

我很想知道如何通过在Actor中模拟一些方法用mocked替换真实对象/ actor的方法实现 )来测试Akka Actorfunction。

我用akka.testkit.TestActorRef ;

另外:我试图使用SpyingProducer但目前尚不清楚如何使用它。 (就像我,如果我在其实现中创建了actor,它将与我现在一样)。 谷歌搜索结果不是很冗长 。

我使用powemockitojava 。 但是这没关系。 我很想知道how to do it in principle 任何语言框架下使用任何语言

(所以如果你不知道power / mockito如何工作只是提供你的代码..(请)或完全了解你将如何使用你知道的工具。)

所以,假设我们有一个要测试的Actor:

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

在我们的测试中,我们想要替换getHelloMessage()返回其他内容。

这是我的尝试:

 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 (anyString())).thenReturn("nothing"); // <- expecting result try { // when Future 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"); } } } 

结果:

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

Akka有一个类AutoPilot ,它基本上是演员的一般模拟,具有响应消息和断言消息被发送的能力。 http://doc.akka.io/docs/akka/snapshot/java/testing.html

这是该页面的java示例。 您可以创建一个探针,设置一个可以响应消息的自动导航,并从中获取一个ActorRef ,您可以替换为真正的actor。

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

我没有使用Akka和Java的经验,但我想我在Scala中使用的解决方案也适用于Java。 根本不需要嘲笑任何东西。 在Java中,模拟有时对测试很有用,但我个人的经验/意见是,无论何时需要PowerMock,你都会做错事。

以下是我尝试使用Akka进行测试的方法:

在Scala中,我使用了一个trait(aka接口),其中定义了actor方法。

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

这样,该function可以非常容易地进行unit testing。 对于真正的演员,我试图坚持只实现接收方法。

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

然后在测试actor时,您可以覆盖getHelloMessage实现来执行任何操作。

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

在Java中,你可以做同样的事情。 从Java 8开始,您可以在接口中提供默认方法实现,您可以在子接口中覆盖它们以进行测试。 另一种方法是在测试中将actor子类化为覆盖某些方法以提供可预测的行为。

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

所以我可能不理解这个问题,但你可能不想模仿一个演员,因为模拟的目的是用一个具有调用期望的测试副本替换像dao这样的东西 – 演员并不真正适合这个法案因为它是你扩展而不是依赖的东西 – 模拟真的只适用于真正的依赖。

TestActorRef专门为您提供对底层actor的访问权限 – 在大多数正常情况下,您只能向actor发送消息而不直接在其上调用任何内容。 TestActoRef通过允许您访问Actor的真正真实扩展而不仅仅是您只能访问的ActorRef来消除此限制! 要么 ? 反对(发送或询问)。

我是一名scala开发者,所以洞察力是有希望的。 我不知道具体的java api但它应该没关系。

我的建议是通过actor ref获取真正的Actor对象,然后测试方法或找出通过真实消息获得测试覆盖率的方法。

通过TestActorRef可以更轻松地模拟actor。 您可以使用此代码:

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

注意:ClassToBeMocked – 它是一个你想要模拟的类。 MockedResultActor – 它是一个你想要在模拟后返回的类。 在您的类中实现模拟的基本配置后,可以使用JunitTest运行此命令。 这里给出的代码仅适用于java中的akka​​ actor。