2009-01-23 9 views
12

Ktoś wie o dobrej drodze do jednostki testowej Scala aktorzy? W ogólnym sensie mam aktora, który odbiera wiadomość i wysyła w odpowiedzi inne wiadomości. Odbywa się to na wielu wątkach, a aktor, który nie jest poprawny, może albo wysłać złe wiadomości, albo nie wysłać żadnego komunikatu. Potrzebuję prostego sposobu na stworzenie makiety aktora, który wysyła i odbiera wiadomości do testowanego aktora. Jakieś doświadczenia w tej dziedzinie?Jednostki testujące aktorów scala

Odpowiedz

5

Ze względu na dynamiczny charakter przekazu w stylu aktorskim, kpiny z aktorów zwykle nie stanowią problemu. Po prostu stwórz aktora, który odbiera pożądaną wiadomość i jesteś w domu wolny. Oczywiście musisz upewnić się, że ten fałszywy aktor jest tym, któremu przekazywane są wiadomości, ale to nie powinno stanowić problemu, o ile aktor, który próbujesz przetestować, jest reentrant.

3

myślę złożoność zależy od czynników paru ...

  1. Jak Stateful jest aktorem?

Jeśli zachowuje się jak funkcja idempotent, tylko asynchroniczna, to powinna być prosta sprawa kpienia z aktora, który wysyła wiadomość, a następnie sprawdza, czy odbiera oczekiwane komunikaty. Prawdopodobnie chcesz użyć elementu reaguj/odbierz na symulowanym aktorze, na wypadek gdyby w rozsądnym czasie nastąpiła reakcja, zamiast zawieszasz.

Jeśli jednak wiadomości nie są od siebie niezależne, należy przetestować je za pomocą różnych sekwencji komunikatów i oczekiwanych wyników.

  1. Ile aktorów będzie testowanych przez aktorów?

Jeśli od aktora oczekuje się interakcji z wieloma innymi osobami i jest to stanowy, należy przetestować go z kilkoma aktorami wysyłającymi/odbierającymi wiadomości. Ponieważ prawdopodobnie nie masz żadnej gwarancji na kolejność, w której pojawią się wiadomości, powinieneś być pewien, że albo permutujesz rozkazy, w których aktorzy wysyłają wiadomości, albo wprowadzają losowe pauzy u aktorów generujących komunikaty i przeprowadzają test wiele razy.

Nie znam się na żadnych gotowych frameworkach do testowania aktorów, ale można się też spodziewać inspiracji dla Erlang.

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

1

Zastanawiałem się, jak testować aktorzy siebie.

Oto, co wymyśliłem, czy ktoś widzi problemy z tym podejściem?

Zamiast wysyłać wiadomości bezpośrednio, a co jeśli Twój aktor delegował wysyłanie wiadomości do funkcji?

Zmodyfikowane testy mogą zamienić się funkcję z jednym, który śledzi liczbę razy zadzwonił i/lub argumentów, z którymi metodę o nazwie:

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 
    } 
    } 

Twój przypadek testowy może zawierać kod jak:

// 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

Myślę, że Daniel odnosi się do zastąpienia celu 1 i celu 2 w powyższym teście fałszywym aktorem zbudowanym w ten sposób: var target1 = aktor {reaguj {case OUTGOING_1 (ładunek) {/ * ustaw flagę lub dołącz ładunek. . * /}}} Ale moje wiadomości często zawierają tylko identyfikatory aktorów. Istnieje aktor katalogu, który przekazuje wiadomości do aktora z kluczem identyfikatora. Zamiast zamieniać implementację sendMessage, zamieniłabym relayMessage. Aby zachować powyższy kod powyżej, zostawiłem to wszystko. Ale ogólny pomysł jest taki sam. –

1

Moja próba jednostkowego przetestowania aktora (działa). Używam Specs jako ramy.

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 
    } 

Jak to działa, wysyłając połączenie synchroniczne do singleton ChatController. ChatController odpowiada za pomocą odpowiedzi (). Odpowiedź jest wysyłana jako zwrot wywoływanej funkcji, która jest zapisywana w mos. Następnie stosuje się dopasowanie, aby uzyskać klasę case, która została wysłana z ChatController. Jeśli wynik jest zgodny z oczekiwaniami (SendFriends), ustaw res na true. Twierdzenie musi być Twierdzeniem określającym powodzenie lub niepowodzenie testu.

Moja aktor Singleton, że jestem testowania

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 
} 

choć jej nie dokończyć to nie zwraca klasę case Sendfriends jak oczekiwano.

0

Pakiet do testowania jednostkowego aktorów został ostatnio dodany do Akka. Możesz znaleźć informacje i fragmenty kodu w blogu this.

Powiązane problemy