actor測試需瞭解scalatest
,在多節點測試時,還須要使用sbt進行。html
scalatest是一個特別針對scala語言設計的單元測試框架,除了提供必要的基類和斷言系統外,scalatest
能夠與IntelliJ IDEA
和maven
等IDE或構建工具集成。akka提供的測試框架就是在scalatest
的基礎上構建的,因此有必要先了解scalatest
。node
scalatest提供了多種測試代碼的書寫風格,因爲akka的例子大可能是WordSpec
風格的,因此建議優先研究和掌握WordSpec
風格。apache
只要是安裝有scala語言插件的IntelliJ IDEA
,是默承認以繼承scalatest的。這意味着你能夠經過右鍵測試代碼,就會彈出Run Test ...
,甚至能夠Debug Test ...
。並且無需像JUnit那樣爲測試類或方法書寫註解。這些註解在scalatest提供的trait中已經包含了。框架
在與maven
集成時,須要在pom
中引入插件,這樣能夠經過mvn test
來運行測試jvm
<!-- disable surefire --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.7</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <!-- enable scalatest --> <plugin> <groupId>org.scalatest</groupId> <artifactId>scalatest-maven-plugin</artifactId> <version>1.0</version> <configuration> <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory> <junitxml>.</junitxml> <filereports>WDF TestSuite.txt</filereports> </configuration> <executions> <execution> <id>test</id> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin>
akka提供了一個特殊的測試框架akka-testkit
maven
<dependency> <groupId>com.typesafe.akka</groupId> <artifactId>akka-testkit_2.12</artifactId> <version>2.5.13</version> <scope>test</scope> </dependency>
官方例子:ide
import akka.actor.ActorSystem import akka.testkit.{ ImplicitSender, TestActors, TestKit } import org.scalatest.{ BeforeAndAfterAll, Matchers, WordSpecLike } class MySpec() extends TestKit(ActorSystem("MySpec")) with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll { override def afterAll { TestKit.shutdownActorSystem(system) } "An Echo actor" must { "send back messages unchanged" in { val echo = system.actorOf(TestActors.echoActorProps) echo ! "hello world" expectMsg("hello world") } } }
afterAll
actorsystem
,能夠向上述代碼那樣,建立actor,發消息。經過expectXXX來斷言收到消息。這裏隱含一個叫testActor
的實例,這個實例是做爲發送消息的源,當書寫expectXXX時,實際是指望testActor
收到消息。ImplicitSender
用於將testActor
映射到self
上sbt是scala官方的構建系統。之因此要學習sbt,是由於想進行akka多節點測試
的話,目前必須採用sbt構建。由於akka官方只爲sbt建立了多節點測試的測試框架。工具
sbt的內容這裏不詳述,從下面多節點測試中體會。單元測試
官方文檔描述瞭如何進行akka的多節點測試。學習
首先,應該引入akka的多節點測試框架:
libraryDependencies += "com.typesafe.akka" %% "akka-multi-node-testkit" % "2.5.13"
而後引入插件,在project/plugins.sbt
中加入:
addSbtPlugin("com.typesafe.sbt" % "sbt-multi-jvm" % "0.4.0")
最終build.sbt
應該看起來是這樣的:
ThisBuild / version := "0.1.0" ThisBuild / scalaVersion := "2.11.8" ThisBuild / organization := "com.eoi.dc" lazy val eoilib = (project in file(".")) .enablePlugins(MultiJvmPlugin) .configs(MultiJvm) .settings( name := "eoilib", libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-cluster" % "2.5.13", "org.scalactic" %% "scalactic" % "3.0.5", "org.scalatest" %% "scalatest" % "3.0.5", "com.typesafe.akka" %% "akka-multi-node-testkit" % "2.5.13", ) )
測試代碼以下:
package com.eoi.dc.lib.test //#config import akka.remote.testkit.MultiNodeConfig object MultiNodeSampleConfig extends MultiNodeConfig { val node1 = role("node1") val node2 = role("node2") } //#config //#spec import akka.actor.{Actor, Props} import akka.remote.testkit.MultiNodeSpec class MultiNodeSampleSpecMultiJvmNode1 extends MultiNodeSample class MultiNodeSampleSpecMultiJvmNode2 extends MultiNodeSample object MultiNodeSample { class Ponger extends Actor { def receive = { case "ping" => sender() ! "pong" } } } class MultiNodeSample extends MultiNodeSpec(MultiNodeSampleConfig) with STMultiNodeSpec { import MultiNodeSample._ import MultiNodeSampleConfig._ def initialParticipants = roles.size "A MultiNodeSample" must { "wait for all nodes to enter a barrier" in { enterBarrier("startup") } "send to and receive from a remote node" in { runOn(node1) { enterBarrier("deployed") val ponger = system.actorSelection(node(node2) / "user" / "ponger") ponger ! "ping" import scala.concurrent.duration._ expectMsg(10.seconds, "pong") } runOn(node2) { system.actorOf(Props[Ponger], "ponger") enterBarrier("deployed") } enterBarrier("finished") } } } //#spec
運行時:
> multi-jvm:testOnly MultiNodeSampleSpec
注意到代碼中MultiNodeSampleSpecMultiJvmNode1
和MultiNodeSampleSpecMultiJvmNode2
這兩個類是有命名約定的,{TestName}MultiJvm{node},這裏的TestName
對應測試名,而node對應MultiNodeConfig
的
val node1 = role("node1") val node2 = role("node2")
在具體的測試代碼中,咱們能夠經過runOn
來控制代碼在哪一個節點上運行。經過enterBarrier
來同步多個節點的代碼運行(簡單的說就是,enterBarrier
將當前節點放入某種狀態,狀態名隨便定義,只有當全部的節點都進入這個狀態後,代碼才能運行下去,不然就block。