Вот как я решил эту проблему.
-> Как запустить родительский актор, которому требуется внедрение зависимостей. Прежде всего, запуск такого актора вручную невозможен, если вам, как и мне, необходимо внедрить зависимость экземпляра, который у вас нет. знаю, как пройти и откуда. Решение состоит в том, чтобы позволить Guice запускать актера автоматически. Вот как это сделать. Сначала создайте модуль подшивки для Guice:
class MyModule extends AbstractModule with AkkaGuiceSupport{
override def configure(): Unit = {
bindActor[Root](Root.NAME)
bind(classOf[StartupActors]).asEagerSingleton()
}
}
Затем сообщите Play, где находится ваш модуль связывания, добавив в файл conf / application.conf следующее:
play.modules={
enabled += "my.path.to.MyModule"
}
StartupActors - это просто класс, который я использую для ведения журнала всякий раз, когда действительно происходит автоматический запуск акторов, внедренных зависимостями. Я регистрирую событие, чтобы быть уверенным, когда и произойдет ли оно:
class StartupActors @Inject() (@Named(Root.NAME) root: ActorRef) {
play.api.Logger.info(s"Initialised $root")
}
Актер Root в моем случае заботится о разборе настраиваемой конфигурации. Поскольку результирующие вары в результате синтаксического анализа требуются моему родительскому субъекту, и во время тестов мне нужно имитировать такие результирующие вары, я делегирую синтаксический анализ другому субъекту, а не родительскому субъекту, то есть корневому субъекту:
object Root {
final val NAME = "THERoot"
case class ParseConfiguration()
}
class Root @Inject()(configuration: Configuration, projectDAO: ProjectDAO) extends Actor {
val resultingVar: Something = myConfigParsing()
override def preStart(): Unit = {
context.actorOf(Props(new MyParent(resultingVar: Something, somethingElse: SomethingElse, projectDAO: ProjectDAO)))
}
override def receive: Receive = {
case ParseConfiguration => sender ! myConfigParsing()
case _ => logger.error("Root actor received an unsupported message")
}
}
Сообщение ParseConfiguration используется исключительно в целях тестирования. Обычно анализ конфигурации происходит из-за инициализации атрибута resultVar.
Таким образом, MyParent не нужно будет ничего вводить. Будут введены только StartupActors и Root. MyParent просто получит projectDAO от Root и передаст его всем своим потомкам.
class MyParent(something: Something, somethingElse: SomethingElse, projectDAO: ProjectDAO) extends Actor { ... }
Наконец, в завершение, я сообщаю здесь, как я писал тесты, так как у меня также были проблемы с поиском достаточной информации в Интернете по этому поводу.
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import com.typesafe.config.ConfigFactory
import org.mockito.Mockito.mock
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
import org.specs2.matcher.MustMatchers
import play.api.Configuration
import scala.concurrent.ExecutionContext
class RootSpec(_system: ActorSystem) extends TestKit(_system)
with WordSpecLike with BeforeAndAfterAll with MustMatchers {
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.global
val conf: com.typesafe.config.Config = ConfigFactory.load()
val configuration: Configuration = Configuration(conf)
val projectDAOMock: ProjectDAO = mock(classOf[ProjectDAO])
private var mainActor: ActorRef = _
private var something: Something = Something.empty
def this() = this(ActorSystem("MySpec"))
override def afterAll: Unit = {
system.shutdown()
}
override def beforeAll(): Unit = {
mainActor = system.actorOf(Props(new Root(configuration, projectDAOMock)), Root.NAME)
}
"RootSpec: Root Actor" should {
val probe = TestProbe()
"successfully parse the configuration file" in {
probe.send(mainActor, ParseConfiguration)
something = probe.expectMsgPF() {
case msg => msg.asInstanceOf[Something]
}
}
}
}
а затем я тестирую MyParent, удобно предоставляя имитирующие объекты вместо варов, полученных в результате синтаксического анализа конфигурации:
import akka.actor.{ActorRef, ActorSystem, Props}
import akka.testkit.{TestKit, TestProbe}
import org.mockito.Mockito
import org.mockito.Mockito._
import org.scalatest.{BeforeAndAfterAll, WordSpecLike}
import org.specs2.matcher.MustMatchers
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{ExecutionContext, Future}
case class AnyProjectAPI(val projectAPI: ProjectAPI) extends AnyVal
class MyParentSpec(_system: ActorSystem, implicit val ec: ExecutionContext) extends TestKit(_system)
with WordSpecLike with BeforeAndAfterAll with MustMatchers {
val something = mock(classOf[Something])
val somethingElse = mock(classOf[somethingElse])
val projectDAOMock: ProjectDAO = mock(classOf[ProjectDAO])
val projectTest: ProjectAPI = new ProjectAPI(allMyRandomConstructorArguments),
val projectsList: List[ProjectAPI] = List(projectTest)
val expectedCreationId = 1
private var parent: ActorRef = _
def this() = this(ActorSystem("MySpec"), scala.concurrent.ExecutionContext.global)
override def afterAll: Unit = {
system.shutdown()
}
override def beforeAll(): Unit = {
parent = system.actorOf(Props(new MyParent(something, somethingElse, projectDAOMock)), MyParent.NAME)
}
"MyParentTesting: parent's pull request" should {
when(myProjApi.getAllProjects).thenReturn(Future {projectsList})
val anyProject: AnyProjectAPI = AnyProjectAPI(org.mockito.Matchers.any[ProjectAPI])
Mockito.when(projectDAOMock.create(org.mockito.Matchers.any[ProjectAPI]))
.thenReturn(Future {expectedCreationId}: Future[Int])
val probe = TestProbe()
val probe1 = TestProbe()
"be successfully satisfied by all children when multiple senders are waiting for an answer" in {
probe.send(parent, UpdateProjects)
probe1.send(parent, UpdateProjects)
allChildren.foreach(child =>
probe.expectMsg(expectedCreationId))
allChildren.foreach(child =>
probe1.expectMsg(expectedCreationId))
}
}
}
person
iammyr
schedule
21.05.2017