12

アクターでいくつかのメソッドをモックする(実際のオブジェクト/アクターのメソッド実装をモックされたものに置き換える) ことによって、Akka アクターの機能をテストする方法について知りたいと思っています。

私は使用しakka.testkit.TestActorRefます;

また、使用しようとしましたが、使用SpyingProducer方法が明確ではありません。(私のように、その実装内でアクターを作成した場合、それは今と同じになります)。それについてのグーグル検索結果はあまり冗長ではありません。

と を使用powemockitojavaます。しかし、それは問題ではありません。どんなフレームワークを持つどんな言語でも知りたいですhow to do it in principle

(したがって、power/mockitoがどのように機能するかわからない場合は、コードを提供してください..(お願いします)、または知っているツールでそれを行う方法についての完全なアイデア。)

では、テストするアクタがあるとします。

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

結果:

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

4 に答える 4

2

Java で Akka を使用した経験はありませんが、Scala で使用するこのソリューションは Java にも適用できると思います。何も嘲笑する必要はまったくありません。Java のモックはテストに役立つ場合がありますが、私の個人的な経験/意見では、PowerMock が必要なときはいつでも何か間違ったことをしているということです。

Akka を使用してテストする方法は次のとおりです。

Scala では、アクター メソッドが定義されているトレイト (別名インターフェイス) を使用します。

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

このようにして、この機能を非常に簡単に単体テストできます。実際のアクターについては、受信メソッドのみを実装することに固執しようとしています。

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

次に、アクターをテストするときに、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 以降、インターフェイスでデフォルトのメソッド実装を提供できるようになりました。これは、テスト用にサブインターフェイスでオーバーライドできます。もう 1 つの方法は、テストでアクターをサブクラス化し、いくつかのメソッドをオーバーライドして予測可能な動作を提供することです。

// 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());
  //        }
  //    }
}
于 2015-04-02T06:29:29.583 に答える
0

TestActorRef を使用すると、アクターを簡単にモックできます。このコードを使用できます:

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

注: ClassToBeMocked -- モックしたいクラスです。MockedResultActor -- モック後に返したいクラスです。これは、クラスでモッキングの基本的な構成を実装した後、JunitTest を使用して実行できます。ここに示すコードは、Java のみの akka アクターに固有のものです。

于 2015-10-12T06:00:35.027 に答える