[From] http://www.algorithmdog.com/akka-test

通过上一篇文章,我们已经大致了解怎么使用 Akka,期待细致用法。这篇文章将介绍如何用 Akka-testkit 对 Akka 程序进行测试。

并行程序是最难调试的程序类型之一,因此做好测试是相当重要的事情。为了减轻 Akka 的程序的测试难度, Akka 官方专门开发了一个测试工具包 Akka-testkit。

1 Actor 的测试需求

对于一个 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 ! "历史上规模最大的众筹行动是什么?";
}
}

2 不适用的 Scalatest

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 测试包应运而生。

3 Akka-testkit 的使用

Maven 项目要使用 Akka-testkit,需要在 pom.xml 文件中加入 akka-testkit 包,如下所示。

<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-testkit_2.10</artifactId>
<version>2.2.5</version>
</dependency>

然后编写单元测试的代码,其基本范例如下。

@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 内部状态的改变。

3.1 回应消息的测试

对于被测 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("关闭闹钟")
}

3.2 内部状态的测试

对于被测 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 变量是否符合预期。
}
}

3.3 发出消息的测试

对于被测 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 是否收到预期问题
}
}

4 总结

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:大牛们轻喷)。最后欢迎关注我的公众号,每两周的更新就会有提醒哦~

[转] Akka 使用系列之二: 测试的更多相关文章

  1. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  3. 不容易系列之二[HDU2042]

    不容易系列之二 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  4. HDU 2042 不容易系列之二 [补6.24] 分类: ACM 2015-06-26 20:40 9人阅读 评论(0) 收藏

    不容易系列之二 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  5. 在KVM虚拟机中使用spice系列之二(USB映射,SSL,密码,多客户端支持)

    在KVM虚拟机中使用spice系列之二(USB映射,SSL,密码,多客户端支持) 发布时间: 2015-02-27 00:16 1.spice的USB重定向 1.1 介绍 使用usb重定向,在clie ...

  6. CDC不同模式在ODI体现系列之二 异步模式

    CDC不同模式在ODI体现系列之二 异步模式 2 异步模式需要在数据库中做一些准备工作: 改数据为归档并启用logminer: SQL> shutdown immediate 数据库已经关闭. ...

  7. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级) 本章介绍的是企业库加密应用程序模块 ...

  8. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (初级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (初级) 企业库加密应用程序模块提供了2种方 ...

  9. Red Gate系列之二 SQL Source Control 3.0.13.4214 Edition 数据库版本控制器 完全破解+使用教程

    原文:Red Gate系列之二 SQL Source Control 3.0.13.4214 Edition 数据库版本控制器 完全破解+使用教程 Red Gate系列之二 SQL Source Co ...

随机推荐

  1. Oracle 错误集锦

    1.plsql进行更新操作时卡死的解决办法 https://blog.csdn.net/laoyingat/article/details/79379270

  2. C程序设计语言(2)文摘

    第一章 导言 1.1 入门 1.2 变量与算术表达式 1.3 for语句 1.4 符号常量 1.5 字符输入输出 #include "stdafx.h" main(int argc ...

  3. Gym 101201I Postman (贪心)

    题意:有个邮递员,要送信,每次最多带 m 封信,有 n 个地方要去送,每个地方有x 封要送,每次都到信全送完了,再回去,对于每个地方,可以送多次直到送够 x 封为止. 析:一个很简单的贪心,就是先送最 ...

  4. swoole1.8.0+版本异步redis安装(本实例为swoole1.8.10版本)详解

    Swoole-1.8.0+版本增加了对异步Redis客户端的支持,基于redis官方提供的hiredis库实现.Swoole提供了__call魔术方法,来映射绝大部分Redis指令(本次安装实例为sw ...

  5. VS2015安装失败

    [16D4:18C8][2017-06-24T13:44:01]e000: Error 0x80091007: Hash mismatch for path: D:\Visual Studio 201 ...

  6. How do I determine if I'm being run under the debugger?

    #include <assert.h>#include <stdbool.h>#include <sys/types.h>#include <unistd.h ...

  7. 软件项目第一个Sprint评分

    第一组 跑男 跑男组他们设计的是极速蜗牛小游戏,他们的界面背景图片做的挺漂亮,现在为止也实现了大部分功能, 但是我没有太听懂他们的游戏规则. 因为蜗牛出发后,每次碰到屏幕边缘后都会有确定的反弹结果,也 ...

  8. ------------------java collection 集合学习 ----小白学习笔记,,有错,请指出谢谢

    <!doctype html>java对象集合学习记录 figure:first-child { margin-top: -20px; } #write ol, #write ul { p ...

  9. cesium编程中级(二)源码编译

    cesium编程中级(二)源码编译 有些情况下,比如我们自己从Github下载了最新的代码,或者自己临时修改了一点代码,想要编译后的Build文件夹的内容,需要自行编译源码,这里介绍一下编译的方法 下 ...

  10. 巧用网页开发者工具F12 审查、修改元素、去除广告、屏蔽遮罩

    巧用网页开发者工具F12 审查.修改元素.去除广告.屏蔽遮罩 每当打开一个网页的时候,是否为页面有很多广告而烦恼:是否为要操作页面(例如观看超清视频),请先注册登录等等事情而麻烦:是否对网页加锁的视频 ...