Scala演员单元测试

12

有人知道如何对Scala actors进行单元测试的好方法吗?从一般的角度来看,我有一个接收消息并将响应消息发送出去的actor。这是在多个线程上完成的,而不正确的actor可能会发送错误的消息或根本不发送消息。我需要一种简单的方式创建一个模拟actor,向被测试的actor发送和接收消息。有关此方面的任何经验?

5个回答

5
由于演员式消息传递的动态特性,模拟演员通常不会有任何问题。只需创建一个接收所需消息的演员即可轻松完成。当然,您需要确保此模拟演员是要传递消息的演员,但只要您尝试测试的演员是可重入的,这应该不是问题。

3

我认为复杂性取决于几个因素...

  1. Actor有多少状态?

如果它的行为类似于幂等函数,仅异步,则应该简单地模拟一个Actor发送消息,然后检查是否收到了预期的消息。你可能想在模拟Actor上使用react/receiveWithin,以防在合理的时间内没有响应,你可以失败而不是挂起。

但是,如果消息彼此不独立,则应使用各种消息序列和预期结果进行测试。

  1. 被测试的Actor将与多少其他Actor交互?

如果期望一个Actor与许多其他Actor进行交互,并且它是有状态的,则应该使用多个Actor发送/接收消息进行测试。由于您可能无法保证消息到达的顺序,所以一定要对Actor发送消息的顺序进行排列或引入随机暂停,并运行测试多次。

我不知道有任何预构建的框架来测试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.

我认为Daniel指的是在上面的测试中用像这样构建的模拟actor替换target1和target2:var target1 = actor { react { case OUTGOING_1(payload){ /* set a flag or append payload... */ }}} 但我的消息通常只包含actor id。有一个Directory actor,它将消息中继到由id键控的actor。因此,我不会交换sendMessage的实现,而是会替换relayMessage。为了使上面的代码更简单,我把所有这些都省略了。但是总体思路是相同的。 - David Carlson

1

我尝试对一个Actor进行单元测试(它可以工作)。我正在使用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通过使用reply()进行响应。响应作为被调用函数的返回值发送,该返回值存储到mos中。然后将mos应用于匹配,获取从ChatController发送的case类。如果结果是预期的(SendFriends),则将res设置为true。res must beTrue断言确定测试的成功或失败。

我正在测试的演员单例

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中添加了一个Actor单元测试套件。您可以在this的博客文章中找到一些信息和代码片段。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接