[From] http://www.algorithmdog.com/akka-testgit
经过上一篇文章,咱们已经大体了解怎么使用 Akka,期待细致用法。这篇文章将介绍如何用 Akka-testkit 对 Akka 程序进行测试。github
并行程序是最难调试的程序类型之一,所以作好测试是至关重要的事情。为了减轻 Akka 的程序的测试难度, Akka 官方专门开发了一个测试工具包 Akka-testkit。工具
对于一个 Actor, 咱们要测什么呢?不一样的文章有不一样的说法,好比 http://rerun.me/2014/09/29/akka-notes-logging-and-testing/ 就把 Actor 测试需求分为:1)发送消息给 Actors, 2)测试内部状态,3)测试日志和 4)带参数 Actor 的测试。我我的认为,对于一个 Actor, 咱们要测的有三个方面:1)Actor 接收消息以后,是否返回正确的消息,2)Actor 接收消息以后,是否正确地改变内部状态和执行内部行为,3)Actor 接收消息以后,是否正确地发消息给后续 Actor。固然这是个人一家之言,有什么不完善的地方,欢迎你们讨论。下面是一个简单的示例图。单元测试
下面是 studentActor 的一段代码,反应了 studentActor 接受到早上时间消息以后的动做,包括:1)给环境或者闹钟回应“关闭闹钟”,2)内部变量 DayInSchool 加 1,3)向老师发送问题消息。这段代码将包含全部要测试的元素,后面咱们将示例怎么用 Akka-testkit 测试这段代码。学习
def receive = { case time:Long => { val originalSender = sender; sender ! "关闭闹钟" DayInSchool += 1; log.info("DayInSchool is %d".format(DayInSchool)) remoteTeacherRef ! "历史上规模最大的众筹行动是什么?"; } }
Scalatest 是 Scala 开发者们最多见的测试工具,其用法很是简便。下面是一个 Scalatest 的简单示例。测试
@RunWith(classOf[JUnitRunner]) class TeacherServiceTest1 extends FunSuite with BeforeAndAfter{ test("countAnswer"){ assert(1==1) } }
可是咱们没法使用 scalatest 测试 Actor。缘由在于:1)Scalatest 没法捕捉被测 Actor 回应的消息,所以没法测试被测 Actor 是否正确回应消息; 2)Scalatest 没法获取被测 Actor 的内部状态,所以没法测试被测 Actor 内部状态的改变是否正确; 3) Scalatest 没法捕捉被测 Actor 对外发送的消息,所以没法测试被测 Actor 对外发送的消息是否正确。所以有必要针对 Akka 开发一套测试工具, Akka-testkit 测试包应运而生。ui
Maven 项目要使用 Akka-testkit,须要在 pom.xml 文件中加入 akka-testkit 包,以下所示。this
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-testkit_2.10</artifactId> <version>2.2.5</version> </dependency>
而后编写单元测试的代码,其基本范例以下。spa
@RunWith(classOf[JUnitRunner]) // class StudentActorTest extends TestKit(ActorSystem("SummerSchool", ConfigFactory.parseString("""一些配置"""))) with WordSpecLike with BeforeAndAfterAll{ "A actor " must { "acts in this way" in { 测试代码写在这里。 } } }
Akka-testkit 的主要工具包括, 1) testProbe 用于测试 Actor 回应和发送消息,testActor 用于简便状况下测试 Actor 回应消息,和 2) testActorRef 用于测试 Actor 内部状态的改变。scala
对于被测 Actor 是否正确地回应消息,能够用 testProbe 测试。首先将 testProbe 给被测 Actor 发送消息,再看 testProbe 是否接受到指望的回应消息。下面是一个示例。
//test its responses "The countAnswer " must { "response a correct answer order" in { val studentActor = system.actorOf(Props(new StudentActor(teacherActor ))) val testProb = new TestProbe(system); testProb.send(studentActor, 7.toLong); //testProbe 给 studentActor 发送 “早上 7 点啦” 的消息 testProb.send(studentActor, 7.toLong) testProb.expectMsg("关闭闹钟") //测试 testProbe 是否收到预期回应消息 testProb.expectMsg("关闭闹钟") } }
除了使用 testProbe 以外,Akka-testkit 还提供一种简便方法: 使用 testActor。 若是测试类实现特质 ImplicitSender, studentActorRef ! 7.toLong 发送给 studentActor 的消息 7.toLong 就是从 testActor 来的。而后在调用 expectMsg(“关闭闹钟”) 就能够测试 testActor 是否收到 studentActor 回应消息 “关闭闹钟” 了。具体代码以下所示。
class StudentActorTest extends TestKit(ActorSystem("SummerSchool", ConfigFactory.parseString("""一些配置"""))) with ImplicitSender //加这句,把testActor 设置为消息发出 Actor with WordSpecLike with BeforeAndAfterAll{ "StudentActor" must { "response correctly" in { val studentActorRef = system.actorOf(Props(new StudentActor(teacherActor ))) studentActorRef ! 7.toLong; //testActor 发出 7.toLong 消息给 studentActor expectMsg("关闭闹钟") //testActor 应该收到 studentActor 回应消息 "关闭闹钟" } } }
咱们能够看出,使用 testActor 的代码比使用 testProbe 的简便。可是,一个东西的用法越是简便,功能便越缺失。testActor 最大的缺失是只能接受被测 Actor 发来的一个回应消息。好比下面的代码就会报错。
"StudentActor" must { "response correctly" in { val studentActorRef = system.actorOf(Props(new StudentActor(teacherActor ))) studentActorRef ! 7.toLong; studentActorRef ! 8.toLong; expectMsg("关闭闹钟") expectMsg("关闭闹钟") }
对于被测 Actor 内部状态的改变,能够用 TestActorRef 进行测试。TestActorRef.underlyingActor 能够探测被测 Actor 的内部,用于测试被测 Actor 内部状态是否符合预期。 下面是一个示例。
"StudentActor" must { "increase the DayInSchool" in { val testActorRef = TestActorRef(new StudentActor(teacherActor)) // 创建 Actor 的TestActorRef testActorRef ! 7.toLong; assert(testActorRef.underlyingActor.DayInSchool == 1); // TestActorRef.underlyingActor 探测 DayInSchool 变量是否符合预期。 } }
对于被测 Actor 是否正确地发出消息,也能够用 testProbe 测试。首先将 testProbe 设置为被测 Actor 发出消息的目标,而后让被测 Actor 发出消息,再看 testProbe 是否接受到指望的消息。下面是一个示例。
"StudentActor " must{ val questionReceiver = TestProbe() val studentActorRef = system.actorOf(Props(new StudentActor(questionReceiver.ref))) // 设置为 studentActor 发送消息的目标 "send a question after waking up" in { studentActorRef ! 7.toLong studentActorRef ! 7.toLong /*给 studentActor 发送消息“7点啦” , *studentActor 会给老师(这里的 questionReceiver.ref) 发送问题 */ questionReceiver.expectMsg("历史上规模最大的众筹行动是什么?") questionReceiver.expectMsg("历史上规模最大的众筹行动是什么?") //模拟老师的 testProbe 是否收到预期问题 } }
Akka-testkit 是 Akka 官方推出的 Akka 测试工具包,用于减轻 Akka 程序的测试难度。Akka-testkit 的主要工具包括, 1) testProbe 用于测试被测 Actor 回应和发送消息,testActor 用于简便状况下测试被测 Actor 回应消息,和 2) testActorRef 用于测试被测 Actor 内部状态的改变。完整的项目代码已经上传到 Github 上了。被测 Actor 是 org.algorithmdog.akkalearning.StudentActor, 测试类是 org.algorithmdog.akkalearning.StudentActorTest。
这篇文章难产了很长一段时间,对不住支持个人读者们。对不起。Akka 和 Actor 模型对我来讲是一个全新的东西,花了比较多的时间学习和熟悉。学习以后,以为第一篇写得太不清楚了,准备重构第一篇。对于这篇文章质量,我我的比较满意的,甚至敢认为这篇文章应该是国内关于 Akka-testkit 最清楚的文章之一(ps:大牛们轻喷)。最后欢迎关注个人公众号,每两周的更新就会有提醒哦~