[轉] Akka 使用系列之二: 測試
[From] http://www.algorithmdog.com/akka-test
通過上一篇文章,我們已經大致瞭解怎麼使用 Akka,期待細緻用法。這篇文章將介紹如何用 Akka-testkit 對 Akka 程式進行測試。
並行程式是最難除錯的程式型別之一,因此做好測試是相當重要的事情。為了減輕 Akka 的程式的測試難度, Akka 官方專門開發了一個測試工具包 Akka-testkit。
1 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:大牛們輕噴)。最後歡迎關注我的公眾號,每兩週的更新就會有提醒哦~