2015-03-26 8 views
8

Sto usando ScalaTest con Akka TestKit per scrivere test di unità e integrazione per un attore che ho codificato semplicemente per inviare un messaggio a un altro attore senza mutare nessuno stato interno. Prendete questo esempio:Come si verifica un attore Akka che invia un messaggio a un altro attore?

class MyActor extends Actor { 
    val anotherActor = context.actorOf(Props[AnotherActor]) 
    def receive: Receive = { 
    case MyMessage => anotherActor ! AnotherMessage 
    } 
} 

Voglio scrivere un test che conferma che anotherActor elaborato AnotherMessage come conseguenza della trasformazione MyActorMyMessage. L'esempio classico è quello di utilizzare TestActorRef per arrivare al l'attore sottostante e verificare la presenza di qualche stato interno che avrebbe dovuto essere colpiti al momento della ricezione messaggio in questo modo:

val testActorRef = TestActorRef(new MyActor) 
"MyActor" should "change some internal state when it receives MyMessage" in { 
    testActorRef ! MyMessage 
    testActorRef.underlyingActor.someState shouldBe "altered" 
} 

Ma nel mio caso io non mi importa di tale stato. In realtà, voglio evitare di tenere qualsiasi stato del genere. TestProbe non era esattamente quello che stavo cercando, dal momento che devi ancora registrare uno TestProbe.ref con l'attore in prova. Per la maggior parte, ho esaminato tutti gli esempi nella documentazione di Akka sui test (http://doc.akka.io/docs/akka/snapshot/scala/testing.html) ma non ho trovato nulla di adatto.

risposta

14

Probabilmente ci sono alcuni modi per farlo, te ne mostrerò uno che funziona quando abbiamo qualcosa di simile al test. Penso ancora che TestActorRef, TestKit e TestProbe siano la strada da percorrere. Considerando la seguente struttura:

case object MyMessage 
case object AnotherMessage 

class MyActor extends Actor { 
    val anotherActor = createAnother 
    def receive: Receive = { 
    case MyMessage => anotherActor ! AnotherMessage 
    } 

    def createAnother = context.actorOf(Props[AnotherActor]) 
} 

class AnotherActor extends Actor{ 
    def receive = { 
    case _ => 
    } 
} 

Il problema è che avete un'istanza attore creazione di un attore bambino e come parte del test è necessario assicurarsi che bambino riceve un messaggio anche se nel test non avere alcun controllo nella creazione di quel bambino. Quando abbiamo questa situazione, dobbiamo fare qualcosa di semplice come il seguente (fatto utilizzando specs2, ma dovrebbe essere in grado di creare qualcosa di simile in ScalaTest):

import akka.actor._ 
import akka.testkit._ 
import org.specs2.mutable.SpecificationLike 
import org.specs2.runner.JUnitRunner 
import org.junit.runner.RunWith 

class MessageSendingSpec extends TestKit(ActorSystem("test")) with SpecificationLike{ 

    val probe = TestProbe() 
    val myActor = TestActorRef(new MyActor{ 
    override def createAnother = probe.ref 
    }) 


    "Sending MyMessage to an instance of MyActor" should{ 
    "pass AnotherMessage to the child AnotherActor" in { 
     myActor ! MyMessage 
     probe.expectMsg(AnotherMessage) 
     success 
    } 
    } 
} 

La chiave è che quando si crea l'attore per testare, ho scavalcare il metodo che crea il bambino per fornire la mia sonda. È grezzo, ma anche semplice ed efficace.

+0

Esattamente quello che stavo cercando! Non ho nemmeno pensato di creare attori bambini con un metodo che potrei semplicemente ignorare per inserire il 'TestProbe.ref'. Grazie @cmbaxter, – nmurthy

+0

Come facciamo questo se abbiamo un altro attore creato all'interno del metodo di ricezione? –

+0

Come potrebbe essere implementato in Java? –