[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:大牛們輕噴)。最後歡迎關注個人公衆號,每兩週的更新就會有提醒哦~