首页 文章

在play framework 2.4.x中使用依赖注入测试actor

提问于
浏览
6

如何测试依赖注入创建的actor?在我的应用程序中,我可以通过命名注入获得ActorRef:

public MyClass {
    @Inject
    @Named("ping")
    ActorRef mPingRef;
}

如何在我的测试中获得此参考?

这是我的演员:

public class PingActor extends UntypedActor {
    @Inject
    public PingActor(Configuration configuration) {
         ... // Use config
    }


    @Override
    public void onReceive(Object message) throws Exception {
        if (message instanceof Ping) {
            getSender().tell(new Pong(), getSelf());
        }
    }

    public static class Ping {}
    public static class Pong {}
}

我已经使用自己的模块配置了我的应用程序:

public class MyModule extends AbstractModule implements AkkaGuiceSupport {
    private final Configuration mConfig;

    public MyModule(Environment environment, Configuration configuration){
        this.mConfig = configuration;
    }

    @Override
    protected void configure() {
        bindActor(PingActor.class, "ping");
    }
}

该模块在application.conf中启用:

play.modules.enabled += "com.my.package.MyModule"

2 回答

  • 4

    此解决方案适用于 PlayScala ,但它应与 PlayJava 的机制相同:

    所以我得到了 GuiceModule

    class CommonModule extends AbstractModule with AkkaGuiceSupport {
      override def configure(): Unit = {
        bindActor[SomeActor]("actor-name")
      }
    }
    

    然后测试(我从我的测试中删除了一些东西,所以它可能无法直接编译):

    import akka.actor.{ActorRef, ActorSystem}
    import akka.testkit.{TestKit, TestProbe}
    import module.CommonModule
    import org.specs2.mutable.Specification
    import org.specs2.specification.Scope
    import play.api.inject._
    import play.api.inject.guice.GuiceApplicationBuilder
    import play.api.test.Helpers._
    
    class SwitchUpdateActorSpec extends Specification {
    
      "MyActor" should {
    
        val actorSystem = ActorSystem("test")
        class Actors extends TestKit(actorSystem) with Scope
    
        val app = new GuiceApplicationBuilder(modules = Seq(new CommonModule))
          .overrides(bind[ActorSystem].toInstance(actorSystem))
          .build()
    
    
        "respond with 'ok' upon receiving a message" in new Actors {
          running(app) {
            private val injector: Injector = app.injector
            private val actor: ActorRef = injector.instanceOf(BindingKey(classOf[ActorRef]).qualifiedWith("actor-name"))
    
            val probe = TestProbe()
            actor.tell("hi there!", probe.ref)
    
            probe.expectMsg("ok")
          }
        }
      }    
    }
    

    所以我做的是:

    • 创造新鲜 ActorSystem

    • 在Akka的 TestKit 中包装 actorSystemlibraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.4.1"

    • 使用 GuiceApplicationBuilder 来应用覆盖

    • 然后直接使用 app.injector 来访问我的guice配置的actor

    当你在 MyModule.configure() 方法中查看 bindActor 的实现时,很明显会发生什么:

    def bindActor[T <: Actor: ClassTag](name: String, props: Props => Props = identity): Unit = {
        accessBinder.bind(classOf[ActorRef])
          .annotatedWith(Names.named(name))
          .toProvider(Providers.guicify(Akka.providerOf[T](name, props)))
          .asEagerSingleton()
      }
    
  • 2

    我正在写演员单元测试

    static ActorSystem system;
    static Configuration configuration;
    static MyActor myActor;
    
    @BeforeClass
    public static void setup() {
        Map<String, Object> stringConf = new HashMap<>();
        configuration = new Configuration(stringConf);
    
        system = ActorSystem.apply();
        final Props props = Props.create(MyActor.class, configuration);
        final TestActorRef<MyActor> ref = TestActorRef.create(system, props, "myActor");
        myActor = ref.underlyingActor();
    }
    
    @AfterClass
    public static void teardown() {
        JavaTestKit.shutdownActorSystem(system);
        system = null;
    }
    

    然后你可以调用你的actor中的方法,就好像它是一个普通的java类 . 根据播放框架https://www.playframework.com/documentation/2.5.x/JavaFunctionalTest

    通常最佳做法是仅在功能测试中注入成员并在单元测试中手动创建实例 .

    这就是我在这里做的事情 . 你需要一个依赖

    "com.typesafe.akka" % "akka-testkit_2.11" % "2.4.12" % "test"
    

    为此工作 . 希望这可以帮助 .

相关问题