在单元测试期间,Akka Actor在异常后重新启动

时间:2015-06-02 18:54:19

标签: scala unit-testing akka akka-testkit

我的Actor看起来像

import akka.actor.Status.Failure
import akka.actor.{Actor, ActorLogging, Props}
import akka.event.LoggingReceive

object Runner {
  def props(race: Race) = Props(classOf[Runner], race)
}

class Runner(race: Race) extends Actor with ActorLogging {

  override def receive: Receive = LoggingReceive {
    case Start =>
      sender ! "OK"
      log.debug("running...")
      Thread.sleep(10)
      throw new RuntimeException("MarathonRunner is tired")

    case Failure(throwable) => throw throwable

    case Stop =>
      log.debug("stopping runner")
      context.stop(self)
  }
}

我的测试看起来像

import akka.actor.{Terminated, ActorSystem}
import akka.testkit.{ImplicitSender, TestActorRef, TestKit}
import org.scalatest._
import scala.concurrent.duration._


class RunnerSpec extends TestKit(ActorSystem("testSystem"))
with WordSpecLike
with MustMatchers
with ImplicitSender {
    "must fail with exception" in {
      val runnerRef = TestActorRef(new Runner(new Marathon), "testRunnerException")
      runnerRef ! Start
      expectMsg("OK")
      watch(runnerRef)
      expectMsg(20 millis, Terminated)
    }
}
  • 但是,我在日志中看到它失败了,因为RuntimeException发生后,Actor重新启动。
  • 您可以看到我的Actor未定义任何supervisorStrategy,其父作为
class Coach() extends Actor with ActorLogging {

  val runner = context.actorOf(Runner.props(new Marathon).withDispatcher("my-pinned-dispatcher"), "runner")

  override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy(maxNrOfRetries = 2, withinTimeRange = 5 seconds) {
    case _: RuntimeException =>
      sender ! Start
      Restart
  }

但我不是在这里测试supervisor,事实上Supervisor甚至不是测试的一部分。

这里出了什么问题?

记录

Testing started at 11:48 AM ...
[DEBUG] [06/02/2015 11:48:08.068] [ScalaTest-run] [EventStream(akka://testSystem)] logger log1-Logging$DefaultLogger started
[DEBUG] [06/02/2015 11:48:08.069] [ScalaTest-run] [EventStream(akka://testSystem)] Default Loggers started
[DEBUG] [06/02/2015 11:48:08.072] [testSystem-akka.actor.default-dispatcher-4] [akka://testSystem/system] now supervising Actor[akka://testSystem/system/deadLetterListener#58458639]
[DEBUG] [06/02/2015 11:48:08.075] [testSystem-akka.actor.default-dispatcher-4] [akka://testSystem/system/deadLetterListener] started (akka.event.DeadLetterListener@7b2fe415)
[DEBUG] [06/02/2015 11:48:08.089] [testSystem-akka.actor.default-dispatcher-5] [akka://testSystem/system/testActor1] started (akka.testkit.TestActor@6242009b)
[DEBUG] [06/02/2015 11:48:08.090] [testSystem-akka.actor.default-dispatcher-5] [akka://testSystem/system] now supervising Actor[akka://testSystem/system/testActor1#1776291392][DEBUG] [06/02/2015 11:48:08.249] [testSystem-akka.actor.default-dispatcher-4] [akka://testSystem/user] now supervising TestActor[akka://testSystem/user/testRunnerException]
[DEBUG] [06/02/2015 11:48:08.250] [ScalaTest-run-running-RunnerSpec] [akka://testSystem/user/testRunnerException] started (com.learner.ahka.ruforever.Runner@afdd280)
[DEBUG] [06/02/2015 11:48:08.251] [ScalaTest-run-running-RunnerSpec] [akka://testSystem/user/testRunnerException] received handled message Start
[DEBUG] [06/02/2015 11:48:08.254] [ScalaTest-run-running-RunnerSpec] [akka://testSystem/user/testRunnerException] running...
[DEBUG] [06/02/2015 11:48:08.267] [ScalaTest-run-running-RunnerSpec] [akka://testSystem/user/testRunnerException] now watched by Actor[akka://testSystem/system/testActor1#1776291392]
[ERROR] [06/02/2015 11:48:08.269] [testSystem-akka.actor.default-dispatcher-5] [akka://testSystem/user/testRunnerException] MarathonRunner is tired
java.lang.RuntimeException: MarathonRunner is tired
    at com.learner.ahka.ruforever.Runner$$anonfun$receive$1.applyOrElse(Runner.scala:18)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
    at akka.event.LoggingReceive.apply(LoggingReceive.scala:62)
    at akka.event.LoggingReceive.apply(LoggingReceive.scala:50)
    at scala.PartialFunction$class.applyOrElse(PartialFunction.scala:123)
    at akka.event.LoggingReceive.applyOrElse(LoggingReceive.scala:50)
    at akka.actor.Actor$class.aroundReceive(Actor.scala:467)
    at com.learner.ahka.ruforever.Runner.aroundReceive(Runner.scala:11)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516)
    at akka.actor.ActorCell.invoke(ActorCell.scala:487)
    at akka.testkit.CallingThreadDispatcher.process$1(CallingThreadDispatcher.scala:251)
    at akka.testkit.CallingThreadDispatcher.runQueue(CallingThreadDispatcher.scala:284)
    at akka.testkit.CallingThreadDispatcher.dispatch(CallingThreadDispatcher.scala:208)
    at akka.actor.dungeon.Dispatch$class.sendMessage(Dispatch.scala:123)
    at akka.actor.ActorCell.sendMessage(ActorCell.scala:369)
    at akka.actor.Cell$class.sendMessage(ActorCell.scala:290)
    at akka.actor.ActorCell.sendMessage(ActorCell.scala:369)
    at akka.actor.LocalActorRef.$bang(ActorRef.scala:384)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply$mcV$sp(RunnerSpec.scala:15)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply(RunnerSpec.scala:13)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply(RunnerSpec.scala:13)
    at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
    at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
    at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
    at org.scalatest.Transformer.apply(Transformer.scala:22)
    at org.scalatest.Transformer.apply(Transformer.scala:20)
    at org.scalatest.WordSpecLike$$anon$1.apply(WordSpecLike.scala:953)
    at org.scalatest.Suite$class.withFixture(Suite.scala:1122)
    at com.learner.ahka.ruforever.RunnerSpec.withFixture(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$class.invokeWithFixture$1(WordSpecLike.scala:950)
    at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:962)
    at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:962)
    at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
    at org.scalatest.WordSpecLike$class.runTest(WordSpecLike.scala:962)
    at com.learner.ahka.ruforever.RunnerSpec.runTest(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1021)
    at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1021)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:413)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:401)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
    at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:396)
    at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:483)
    at org.scalatest.WordSpecLike$class.runTests(WordSpecLike.scala:1021)
    at com.learner.ahka.ruforever.RunnerSpec.runTests(RunnerSpec.scala:9)
    at org.scalatest.Suite$class.run(Suite.scala:1424)
    at com.learner.ahka.ruforever.RunnerSpec.org$scalatest$WordSpecLike$$super$run(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1067)
    at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1067)
    at org.scalatest.SuperEngine.runImpl(Engine.scala:545)
    at org.scalatest.WordSpecLike$class.run(WordSpecLike.scala:1067)
    at com.learner.ahka.ruforever.RunnerSpec.run(RunnerSpec.scala:9)
    at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:55)
    at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$3.apply(Runner.scala:2563)
    at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$3.apply(Runner.scala:2557)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:2557)
    at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(Runner.scala:1044)
    at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(Runner.scala:1043)
    at org.scalatest.tools.Runner$.withClassLoaderAndDispatchReporter(Runner.scala:2722)
    at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter(Runner.scala:1043)
    at org.scalatest.tools.Runner$.run(Runner.scala:883)
    at org.scalatest.tools.Runner.run(Runner.scala)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.runScalaTest2(ScalaTestRunner.java:138)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

[DEBUG] [06/02/2015 11:48:08.269] [testSystem-akka.actor.default-dispatcher-5] [akka://testSystem/user/testRunnerException] restarting
[DEBUG] [06/02/2015 11:48:08.272] [testSystem-akka.actor.default-dispatcher-5] [akka://testSystem/user/testRunnerException] restarted

assertion failed: timeout (20 milliseconds) during expectMsg while waiting for Terminated
java.lang.AssertionError: assertion failed: timeout (20 milliseconds) during expectMsg while waiting for Terminated
    at scala.Predef$.assert(Predef.scala:165)
    at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:338)
    at akka.testkit.TestKitBase$class.expectMsg(TestKit.scala:324)
    at akka.testkit.TestKit.expectMsg(TestKit.scala:718)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply$mcV$sp(RunnerSpec.scala:18)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply(RunnerSpec.scala:13)
    at com.learner.ahka.ruforever.RunnerSpec$$anonfun$1.apply(RunnerSpec.scala:13)
    at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
    at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
    at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
    at org.scalatest.Transformer.apply(Transformer.scala:22)
    at org.scalatest.Transformer.apply(Transformer.scala:20)
    at org.scalatest.WordSpecLike$$anon$1.apply(WordSpecLike.scala:953)
    at org.scalatest.Suite$class.withFixture(Suite.scala:1122)
    at com.learner.ahka.ruforever.RunnerSpec.withFixture(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$class.invokeWithFixture$1(WordSpecLike.scala:950)
    at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:962)
    at org.scalatest.WordSpecLike$$anonfun$runTest$1.apply(WordSpecLike.scala:962)
    at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
    at org.scalatest.WordSpecLike$class.runTest(WordSpecLike.scala:962)
    at com.learner.ahka.ruforever.RunnerSpec.runTest(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1021)
    at org.scalatest.WordSpecLike$$anonfun$runTests$1.apply(WordSpecLike.scala:1021)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:413)
    at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:401)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
    at org.scalatest.SuperEngine.org$scalatest$SuperEngine$$runTestsInBranch(Engine.scala:396)
    at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:483)
    at org.scalatest.WordSpecLike$class.runTests(WordSpecLike.scala:1021)
    at com.learner.ahka.ruforever.RunnerSpec.runTests(RunnerSpec.scala:9)
    at org.scalatest.Suite$class.run(Suite.scala:1424)
    at com.learner.ahka.ruforever.RunnerSpec.org$scalatest$WordSpecLike$$super$run(RunnerSpec.scala:9)
    at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1067)
    at org.scalatest.WordSpecLike$$anonfun$run$1.apply(WordSpecLike.scala:1067)
    at org.scalatest.SuperEngine.runImpl(Engine.scala:545)
    at org.scalatest.WordSpecLike$class.run(WordSpecLike.scala:1067)
    at com.learner.ahka.ruforever.RunnerSpec.run(RunnerSpec.scala:9)
    at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:55)
    at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$3.apply(Runner.scala:2563)
    at org.scalatest.tools.Runner$$anonfun$doRunRunRunDaDoRunRun$3.apply(Runner.scala:2557)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:2557)
    at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(Runner.scala:1044)
    at org.scalatest.tools.Runner$$anonfun$runOptionallyWithPassFailReporter$2.apply(Runner.scala:1043)
    at org.scalatest.tools.Runner$.withClassLoaderAndDispatchReporter(Runner.scala:2722)
    at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter(Runner.scala:1043)
    at org.scalatest.tools.Runner$.run(Runner.scala:883)
    at org.scalatest.tools.Runner.run(Runner.scala)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.runScalaTest2(ScalaTestRunner.java:138)
    at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:28)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

1 个答案:

答案 0 :(得分:1)

基于@cmbaxter的评论,我挖了一点并解决了这个问题。我不得不覆盖class RunnerSpec extends TestKit(ActorSystem("testSystem")) with FlatSpecLike with MustMatchers with ImplicitSender { behavior of "A Marathon runner" it must "must fail with exception" in { val supervisorRef = TestActorRef[DummySupervisor] val runnerRef = TestActorRef(Runner.props(new Marathon), supervisorRef, "runnerFail") runnerRef ! Start expectMsg("OK") watch(runnerRef) expectTerminated(runnerRef, 10 millis) } } class DummySupervisor extends Coach { override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy() { case _: RuntimeException => stop } } 以进行测试而不允许它重新启动 这是我的测试看起来像

'NaN'