[轉] Akka 使用系列之二: 測試

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

 

經過上一篇文章,咱們已經大體瞭解怎麼使用 Akka,期待細緻用法。這篇文章將介紹如何用 Akka-testkit 對 Akka 程序進行測試。github

並行程序是最難調試的程序類型之一,所以作好測試是至關重要的事情。爲了減輕 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。固然這是個人一家之言,有什麼不完善的地方,歡迎你們討論。下面是一個簡單的示例圖。單元測試

akka-testkit

下面是 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 測試包應運而生。ui

 

3 Akka-testkit 的使用

      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

 

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

相關文章
相關標籤/搜索