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.

Respuestas a la pregunta(3)

Su respuesta a la pregunta