在《Akka学习笔记:日志》文章中介绍了Akka的日志功能。本文主要介绍如何测试Actors。
StudentSimulatorApp程序做了该做的事情,你肯定同意将它写成一个测试用例。为了减少测试的烦恼, Akka提供了一些很强大的工具包,利用它我们可以做一些很神奇的事情,比如直接探讨Actor内部的实现。
好了,说的够多了!让我们来看下这个测试用例。我们现将StudentSimulatorApp程序改成一个Testcase。如下图:
如果想及时了解
Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号:
iteblog_hadoop
让我们先看下测试用例的声明:
1 | class TeacherPreTest extends TestKit(ActorSystem("UniversityMessageSystem")) |
4 | with BeforeAndAfterAll { |
从TestCase类的声明中我们可以看出:
1、TestKit是一个trait,它接收一个ActorSystem对象,通过ActorSystem我们可以创建Actors。从内部实现来说,TestKit 装饰了ActorSystem,并且替换了默认的dispatcher;
2、我们这里用到了WordSpec,在是编写有趣ScalaTest的一种方式;
3、MustMatchers提供了一种非常方便的方法,使得将testcase 看起来和正常的语言一样;
4、我们嵌入了BeforeAndAfterAll 来在testcases运行完的时候关闭ActorSystem。afterAll 方法看起来和JUnit中的tearDown方法类似。
1, 2 - 给Actor发送消息
1、第一个测试用例仅仅将消息发送给PrintActor,不做任何的检查;
2、第二个测试用例将消息发送到 Log actor,而 Log actor用ActorLogging 的相应方法将消息发送到EventStream,它同样不做任何的检查。测试用例代码如下:
04 | "print a quote when a QuoteRequest message is sent" in { |
06 | val teacherRef = TestActorRef[TeacherActor] |
07 | teacherRef ! QuoteRequest |
12 | "A teacher with ActorLogging" must { |
14 | "log a quote when a QuoteRequest message is sent" in { |
16 | val teacherRef = TestActorRef[TeacherLogActor] |
17 | teacherRef ! QuoteRequest |
3、检查Actors的内部状态
第三个测试用例用TestActorRef 中的underlyingActor 方法,然后在TeacherActor中的quoteList上进行调用,而quoteList 方法仅仅返回quotes list。我们用这个list检查其size。如果调用quoteList 出现了异常,那么请你去检查一下TeacherLogActor类是否写错。代码如下:
07 | "have a quote list of size 4" in { |
09 | val teacherRef = TestActorRef[TeacherLogActor] |
10 | teacherRef.underlyingActor.quoteList must have size (4) |
11 | teacherRef.underlyingActor.quoteList must have size (4) |
4、检查日志消息
正如我们在EventStream和日志那段《Akka学习笔记:日志》进行讨论的。所有的日志消息将发送到EventStream 中,而SLF4JLogger将订阅它,然后将那些消息输入到日志文件/控制台中。如果我们直接在测试用例中直接订阅EventStream 那岂不是很棒?然后在那检查日志消息,看起来我们是可以实现的。这包含两步:
1)、你需要在TestKit上添加额外的配置,如下
1 | class TeacherTest extends TestKit(ActorSystem("UniversityMessageSystem", |
2 | ConfigFactory.parseString("""akka.loggers = ["akka.testkit.TestEventListener"]"""))) |
5 | with BeforeAndAfterAll { |
2)、现在我们已经订阅到EventStream上,我们可以从testcase 直接检查:
2 | "be verifiable via EventFilter in response to a QuoteRequest that is sent" in { |
4 | val teacherRef = TestActorRef[TeacherLogActor] |
5 | EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept { |
6 | teacherRef ! QuoteRequest |
EventFilter.info块仅仅接受以QuoteResponse (pattern='QuoteResponse*)开始的日志。当然,你也可以用start='QuoteResponse'来实现。如果没有消息发送到TeacherLogActor,那么这个测试用例将会失败
5、测试带有构造参数的Actors
需要注意的是,我们在测试用例中创建Actors的方法是通过TestActorRef[TeacherLogActor],而不是通过system.actorOf。这仅仅是为了能够在TeacherActorRef中通过underlyingActor 方法进入到Actor的内部。我们不能通过ActorRef 实现。
如果Actor介绍参数,那么我们创建TestActorRef可以如下:
1 | val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes)) |
整个测试用例看起来如下
02 | " have a quote list of the same size as the input parameter" in { |
05 | "Moderation is for cowards", |
06 | "Anything worth doing is worth overdoing", |
07 | "The trouble is you think you have time", |
08 | "You never gonna know if you never even try") |
10 | val teacherRef = TestActorRef(new TeacherLogParameterActor(quotes)) |
13 | teacherRef.underlyingActor.quoteList must have size (4) |
14 | EventFilter.info(pattern = "QuoteResponse*", occurrences = 1) intercept { |
15 | teacherRef ! QuoteRequest |
关闭ActorSystem
最后,通过afterAll关闭ActorSystem:
1 | override def afterAll() { |