2009-01-23 78 views
12

任何人都知道單元測試斯卡拉演員的好方法嗎?從一般意義上說,我有一個演員接收到一條消息,並會發出其他消息作爲迴應。這是在多個線程上完成的,而一個不正確的參與者可能會發送錯誤的消息或根本沒有消息。我需要一種簡單的方法來創建一個模擬演員,向正在測試的演員發送和接收消息。在這方面有什麼經驗?單元測試斯卡拉演員

回答

5

由於演員風格的消息傳遞的動態性,嘲諷演員通常沒有任何麻煩。只需創建一個接收所需消息的演員,並且您可以免費回家。你當然需要確保這個模擬演員是傳遞消息的演員,但只要你試圖測試的演員是reentrant,這應該不成問題。

3

我認爲複雜性取決於幾個因素...

  1. 如何狀態是演員?

如果它的行爲像一個冪等函數,只是異步的,那麼它應該是一個簡單的事情,嘲笑發送消息的actor,然後檢查它是否收到預期的消息。您可能想要在模擬演員上使用react/receiveWithin,以便在合理的時間段內有響應時您可以失敗而不是掛起。

但是,如果消息不是彼此獨立的,那麼你應該測試它的各種消息序列和預期的結果。

  1. 被測試的演員會與多少個演員互動?

如果一個actor期望與其他人進行交互,並且它是有狀態的,那麼它應該與幾個參與者發送/接收消息一起測試。由於您可能無法保證消息的到達順序,因此您應該確保排列演員發送消息的順序,或者在演員生成消息時引入隨機暫停並多次運行測試。

我沒有意識到任何測試演員的預先構建的框架,但你可以期待Erlang的靈感。

http://svn.process-one.net/contribs/trunk/eunit/doc/overview-summary.html

1

我一直想知道如何測試自己的演員。

這是我想出來的,是否有人看到這種方法的問題?

而不是直接發送消息,如果你的演員委託消息發送到一個函數呢?

那麼你的測試可以換出功能與一個跟蹤與該方法被調用的叫做時間和/或參數的個數:

class MyActor extends Actor { 

    var sendMessage:(Actor, ContactMsg) => Unit = { 
    (contactActor, msg) => { 
     Log.trace("real sendMessage called") 
     contactActor ! msg 
    } 
    } 

    var reactImpl:PartialFunction(Any, Unit) = { 
     case INCOMING(otherActor1, otherActor2, args) => { 

     /* logic to test */ 
     if(args){ 
      sendMessage(otherActor1, OUTGOING_1("foo")) 

     } else { 
      sendMessage(otherActor2, OUTGOING_2("bar")) 
     } 
     } 
    } 

    final def act = loop { 
    react { 
     reactImpl 
    } 
    } 

你的測試用例可能包含如下代碼:

// setup the test 
var myActor = new MyActor 
var target1 = new MyActor 
var target2 = new MyActor 
var sendMessageCalls:List[(Actor, String)] = Nil 

/* 
* Create a fake implementation of sendMessage 
* that tracks the arguments it was called with 
* in the sendMessageCalls list: 
*/ 
myActor.sendMessage = (actor, message) => { 
    Log.trace("fake sendMessage called") 
    message match { 
    case OUTGOING_1(payload) => { 
     sendMessageCalls = (actor, payload) :: sendMessageCalls 
    } 
    case _ => { fail("Unexpected Message sent:"+message) } 
    } 
} 

// run the test 
myActor.start 
myActor.reactImpl(Incoming(target1, target2, true)) 

// assert the results 
assertEquals(1, sendMessageCalls.size) 
val(sentActor, sentPayload) = sendMessageCalls(0) 
assertSame(target1, sentActor) 
assertEquals("foo", sentPayload) 
// .. etc. 
+0

我認爲丹尼爾是指在上面的測試中用一個像這樣構建的模擬角色替換target1和target2: var target1 = actor {react {case OUTGOING_1(payload){/ * set a flag or append payload .. 。* /}}} 但我的消息通常只包含actor id。有一個目錄actor將消息轉發給id所鍵入的actor。因此,而不是交換sendMessage的實現,我會換出relayMessage。爲了讓代碼更簡單,我把所有這些都留下了。但總的想法是一樣的。 – 2009-07-14 20:17:50

1

我試圖在單元測試一個演員(它的工作)。我使用Specs作爲框架。

object ControllerSpec extends Specification { 
    "ChatController" should{ 
    "add a listener and respond SendFriends" in{ 
     var res = false 
     val a = actor{} 
     val mos = {ChatController !? AddListener(a)} 
     mos match{ 
      case SendFriends => res = true 
      case _ => res = false 
     } 
     res must beTrue 
    } 

這是如何工作的,是通過向單身ChatController發送一個同步調用。 ChatController通過使用回覆()來回應。該響應作爲被調用函數的返回被髮送,該函數被存儲到mos中。然後,將匹配應用於從ChatController發送的案例類。如果結果是所期望的(SendFriends),則將res設置爲true。 資源必須是真實的斷言決定了測試的成敗。那我測試

import ldc.socialirc.model._ 

import scala.collection.mutable.{HashMap, HashSet} 
import scala.actors.Actor 
import scala.actors.Actor._ 

import net.liftweb.util.Helpers._ 

//Message types 
case class AddListener(listener: Actor) 
case class RemoveListener(listener: Actor) 
case class SendFriends 
//Data Types 
case class Authority(usr: Actor, role: String) 
case class Channel(channelName: String, password: String, creator: String, motd: String, users: HashSet[Authority]) 

object ChatController extends Actor { 
    // The Channel List - Shows what actors are in each Chan 
    val chanList = new HashMap[String, Channel] 
    // The Actor List - Shows what channels its in 
    val actorList = new HashMap[Actor, HashSet[String]] 

    def notifyListeners = { 

    } 

    def act = { 
     loop { 
      react { 
       case AddListener(listener: Actor)=> 
        actorList += listener -> new HashSet[String] 
        reply(SendFriends) 

      } 
     } 
    } 
    start //Dont forget to start 
} 

雖然它沒有完成它並返回Sendfriends案例類如預期

我的演員單。

0

演員單元測試套件最近已添加到Akka。您可以在this博文中找到一些信息和代碼片段。