Probando a los actores Akka que mezclan Stash con TestActorRef
Estoy teniendo un problema con un actor que extiende Stash y que funciona perfectamente bien al crear instancias con actorOf en un ActorSystem simple. Ahora me gustaría escribir algunas pruebas para mis actores ocultos antes de usarlos en mi programa. Pero no puedo encontrar una manera de usar un TestActorRef con este actor en mi suite de pruebas.
El código que funciona se ve así:
import akka.actor.{Stash, Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object StashTest {
val config = ConfigFactory.parseString(
"""
|akka.actor.default-mailbox {
| mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|}
""".stripMargin)
}
class StashTestActor extends Stash {
def receive: Actor.Receive = {
case "unstash" =>
unstashAll()
context become print
case msg => stash()
}
def print: Actor.Receive = {
case msg => println(s"Unstashed message: $msg")
}
}
val system = ActorSystem("stashSystem", StashTest.config)
val ref = system.actorOf(Props[StashTestActor])
ref ! "stash me"
ref ! "blah"
ref ! "unstash"
Que imprime
Unstashed message: stash me
Unstashed message: blah
Pero si trato de escribir una prueba de WordSpec para este actor, me deja con algunas excepciones desagradables que no puedo averiguar qué les gustaría que cambiara en mi código.
La clase de prueba se ve así.
import akka.testkit.{TestActorRef, TestKit}
import akka.actor.{Stash, Actor, ActorSystem}
import org.scalatest.{WordSpecLike, MustMatchers}
import com.typesafe.config.ConfigFactory
class StashTestActor extends Stash {
def receive: Actor.Receive = {
case "unstash" =>
unstashAll()
context become print
case msg => stash()
}
def print: Actor.Receive = {
case msg => println(s"Unstashed message: $msg")
}
}
class StashTest extends TestKit(ActorSystem("testSystem", StashTest.config))
with WordSpecLike
with MustMatchers {
"A simple stashing actor" must {
val actorRef = TestActorRef[StashTestActor]
"stash messages" in {
actorRef ! "stash me!"
}
"unstash all messages" in {
actorRef ! "unstash"
}
}
}
object StashTest {
val config = ConfigFactory.parseString(
"""
|akka.actor.default-mailbox {
| mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|}
""".stripMargin)
}
Al ejecutar la prueba, recibo las siguientes excepciones que se producen durante la creación de instancias de TestActorRef.
[ERROR] [08/20/2013 14:19:40.765] [testSystem-akka.actor.default-dispatcher-3] [akka://testSystem/user/$a] Could not instantiate Actor
Make sure Actor is NOT defined inside a class/trait,
if so put it outside the class/trait, f.e. in a companion object,
OR try to change: 'actorOf(Props[MyActor]' to 'actorOf(Props(new MyActor)'.
akka.actor.ActorInitializationException: exception during creation
at akka.actor.ActorInitializationException$.apply(Actor.scala:218)
at akka.actor.ActorCell.create(ActorCell.scala:578)
at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:425)
at akka.actor.ActorCell.systemInvoke(ActorCell.scala:447)
at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:262)
at akka.testkit.CallingThreadDispatcher.process$1(CallingThreadDispatcher.scala:244)
at akka.testkit.CallingThreadDispatcher.runQueue(CallingThreadDispatcher.scala:284)
at akka.testkit.CallingThreadDispatcher.register(CallingThreadDispatcher.scala:153)
at akka.dispatch.MessageDispatcher.attach(AbstractDispatcher.scala:133)
at akka.actor.dungeon.Dispatch$class.start(Dispatch.scala:84)
at akka.actor.ActorCell.start(ActorCell.scala:338)
at akka.testkit.TestActorRef.<init>(TestActorRef.scala:50)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:141)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:137)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:146)
at akka.testkit.TestActorRef$.apply(TestActorRef.scala:144)
at stashActorTest.StashTest$anonfun$1.apply$mcV$sp(StashTestActor.scala:29)
at stashActorTest.StashTest$anonfun$1.apply(StashTestActor.scala:28)
at stashActorTest.StashTest$anonfun$1.apply(StashTestActor.scala:28)
at org.scalatest.SuperEngine.registerNestedBranch(Engine.scala:613)
at org.scalatest.WordSpecLike$class.org$scalatest$WordSpecLike$registerBranch(WordSpecLike.scala:120)
at org.scalatest.WordSpecLike$anon$2.apply(WordSpecLike.scala:851)
at org.scalatest.words.MustVerb$StringMustWrapperForVerb$class.must(MustVerb.scala:189)
at org.scalatest.matchers.MustMatchers$StringMustWrapper.must(MustMatchers.scala:6167)
at stashActorTest.StashTest.<init>(StashTestActor.scala:28)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at java.lang.Class.newInstance(Class.java:374)
at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:444)
at sbt.TestRunner.runTest$1(TestFramework.scala:84)
at sbt.TestRunner.run(TestFramework.scala:94)
at sbt.TestFramework$anonQue imprime$anonfun$init$El código que funciona se ve así:$anonfun$apply$8.apply(TestFramework.scala:224)
at sbt.TestFramework$anonQue imprime$anonfun$init$El código que funciona se ve así:$anonfun$apply$8.apply(TestFramework.scala:224)
at sbt.TestFramework$.sbt$TestFramework$withContextLoader(TestFramework.scala:212)
at sbt.TestFramework$anonQue imprime$anonfun$init$1.apply(TestFramework.scala:224)
at sbt.TestFramework$anonQue imprime$anonfun$init$1.apply(TestFramework.scala:224)
at sbt.TestFunction.apply(TestFramework.scala:229)
at sbt.Tests$anonfun$7.apply(Tests.scala:196)
at sbt.Tests$anonfun$7.apply(Tests.scala:196)
at sbt.std.Transform$anonPero si trato de escribir una prueba de WordSpec para este actor, me deja con algunas excepciones desagradables que no puedo averiguar qué les gustaría que cambiara en mi código.$anonfun$apply$2.apply(System.scala:45)
at sbt.std.Transform$anonPero si trato de escribir una prueba de WordSpec para este actor, me deja con algunas excepciones desagradables que no puedo averiguar qué les gustaría que cambiara en mi código.$anonfun$apply$2.apply(System.scala:45)
at sbt.std.Transform$anon$4.work(System.scala:64)
at sbt.Execute$anonfun$submitEl código que funciona se ve así:$anonfun$apply$1.apply(Execute.scala:237)
at sbt.Execute$anonfun$submitEl código que funciona se ve así:$anonfun$apply$1.apply(Execute.scala:237)
at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
at sbt.Execute.work(Execute.scala:244)
at sbt.Execute$anonfun$submit$1.apply(Execute.scala:237)
at sbt.Execute$anonfun$submit$1.apply(Execute.scala:237)
at sbt.ConcurrentRestrictions$anonLa clase de prueba se ve así.$anonfun$1.apply(ConcurrentRestrictions.scala:160)
at sbt.CompletionService$anon$2.call(CompletionService.scala:30)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Caused by: akka.actor.ActorInitializationException: Could not instantiate Actor
Make sure Actor is NOT defined inside a class/trait,
if so put it outside the class/trait, f.e. in a companion object,
OR try to change: 'actorOf(Props[MyActor]' to 'actorOf(Props(new MyActor)'.
at akka.actor.ActorInitializationException$.apply(Actor.scala:218)
at akka.testkit.TestActorRef$anonfun$applyQue imprime$anonfun$apply$1.applyOrElse(TestActorRef.scala:148)
at akka.testkit.TestActorRef$anonfun$applyQue imprime$anonfun$apply$1.applyOrElse(TestActorRef.scala:147)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
at scala.util.Failure$anonfun$recover$1.apply(Try.scala:185)
at scala.util.Try$.apply(Try.scala:161)
at scala.util.Failure.recover(Try.scala:185)
at akka.testkit.TestActorRef$anonfun$apply$2.apply(TestActorRef.scala:147)
at akka.testkit.TestActorRef$anonfun$apply$2.apply(TestActorRef.scala:153)
at akka.actor.CreatorFunctionConsumer.produce(Props.scala:369)
at akka.actor.Props.newActor(Props.scala:323)
at akka.actor.ActorCell.newActor(ActorCell.scala:534)
at akka.actor.ActorCell.create(ActorCell.scala:560)
... 58 more
Caused by: java.lang.NullPointerException
at akka.actor.UnrestrictedStash$class.$init$(Stash.scala:82)
at stashActorTest.StashTestActor.<init>(StashTestActor.scala:9)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at akka.actor.ReflectiveDynamicAccess$anonfun$createInstanceFor$2.apply(DynamicAccess.scala:78)
at scala.util.Try$.apply(Try.scala:161)
at akka.actor.ReflectiveDynamicAccess.createInstanceFor(DynamicAccess.scala:73)
... 64 more
No tengo ningún problema con el uso de TestActorRefs con actores que no extienden Stash. Así que no sé si es un error de configuración o algo más que me estoy perdiendo.