2015-06-09 11 views
10

Próbuję przetestować moich aktorów Akka.NET, ale mam problemy z testKit i zrozumienie, jak to działa.Jak używać TestKit w Akka.NET

Ponieważ nie ma oficjalnej dokumentacji testów jednostkowych w Akka.NET, zbadałem przykład kodu repozytorium Akka.NET, ale przykłady tam zastosowane nie działają dla mnie.

Testy, których użyłem do odniesienia to ReceiveActorTests.cs i ReceiveActorTests_Become.cs, ponieważ są blisko scenariusza, który próbuję przetestować w mojej aplikacji.

Oto niektóre atrapa kodu:

Biorąc pod uwagę ten aktor

public class Greeter : ReceiveActor 
{ 
    public Greeter() 
    { 
     NotGreeted(); 
    } 

    private void NotGreeted() 
    { 
     Receive<Greeting>(msg => Handle(msg)); 
    } 

    private void Greeted() 
    { 
     Receive<Farewell>(msg => Handle(msg)); 
    } 

    private void Handle(Greeting msg) 
    { 
     if (msg.Message == "hello") 
     { 
      Become(Greeted); 
     } 
    } 

    private void Handle(Farewell msg) 
    { 
     if (msg.Message == "bye bye") 
     { 
      Become(NotGreeted); 
     } 
    } 
} 

Chcę przetestować że Odbiera powitania i komunikaty Farewell poprawnie i przejdzie poprawnie Zostań członkowskich. Patrząc na testach ReceiveActorTests_Become.cs, aktorem jest tworzony przez

var system = ActorSystem.Create("test"); 
var actor = system.ActorOf<BecomeActor>("become"); 

i wiadomość jest wysyłana i zapewnił o

actor.Tell(message, TestActor); 
ExpectMsg(message); 

jednak, gdy próbuję to podejście do instancji aktorem, i wielu innych na podstawie metody TestKit (patrz niżej), trzymam uzyskiwanie Samme udało błąd badawczej:

Xunit.Sdk.TrueExceptionFailed: Timeout 00:00:03 while waiting for a message of type ConsoleApplication1.Greeting 
Expected: True 
Actual: False 

To jest mój testu:

public class XUnit_GreeterTests : TestKit 
{ 
    [Fact] 
    public void BecomesGreeted() 
    { 
     //var system = ActorSystem.Create("test-system"); // Timeout error 
     //var actor = system.ActorOf<Greeter>("greeter"); // Timeout error 
     //var actor = ActorOfAsTestActorRef<Greeter>("greeter"); // Timeout error 
     //var actor = ActorOf(() => new Greeter(), "greeter"); // Timeout error 
     //var actor = Sys.ActorOf<Greeter>("greeter"); // Timeout error 
     //var actor = Sys.ActorOf(Props.Create<Greeter>(), "greeter"); // Timeout error 
     var actor = CreateTestActor("greeter"); // Works, but doesn't test my Greeter actor, but rather creates a generic TestActor (as I understand it) 

     var message = new Greeting("hello"); 

     actor.Tell(message, TestActor); 

     ExpectMsg(message); 
    } 
} 

Próbowałem również przesuwać linię ExpectMsg powyżej linii actor.Tell (ponieważ bardziej sensowne było oczekiwanie czegoś, zanim zaczniesz działać i raczej zweryfikowanie oczekiwań po), ale powoduje to również błąd Timeout .

Próbowałem już z testami NUnit i XUnit.

Prawdopodobnie jest coś bardzo podstawowego, czego nie zauważyłem.

+0

Być może zainteresuje Cię to głębokie wprowadzenie do TestKit i jak z niego korzystać. Prekursor do długo zaległych dokumentów na ten temat, który powinien zostać opublikowany w przyszłym tygodniu >> https://petabridge.com/blog/akka-testkit-introduction/ (ujawnienie: napisałem) – AndrewS

Odpowiedz

14

TestKit służy do testowania behawioralnego w celu sprawdzenia, czy aktorzy działają zgodnie z oczekiwaniami w kontekście całego systemu aktorów.To bardziej przypomina testowanie czarnej skrzynki - nie docierasz bezpośrednio do wnętrza aktora. Zamiast tego lepiej skupić się na zachowaniu takim jak przy danym sygnale A i zachowaniu aktora B, powinien on emitować komunikat C do innego aktora D.

W twoim przykładzie problem z aktorem Greeter polega na tym, że jest wyciszony - podczas gdy może odbierać niektóre dane wejściowe, nic nie robi. Z perspektywy całego systemu może być martwy i nikogo to nie obchodzi.

Korzystanie inny przykład - biorąc pod uwagę następujące Aktor:

public class Greeter : ReceiveActor 
{ 
    public Greeter() 
    { 
     Receive<Greet>(greet => 
     { 
      // when message arrives, we publish it on the event stream 
      // and send response back to sender 
      Context.System.EventStream.Publish(greet.Who + " sends greetings"); 
      Sender.Tell(new GreetBack(Self.Path.Name)); 
     }); 
    } 
} 

Stwórzmy specyfikację Przykład testowy:

public class GreeterSpec : TestKit 
{ 
    private IActorRef greeter; 

    public GreeterSpec() : base() 
    { 
     greeter = Sys.ActorOf<Greeter>("TestGreeter"); 
    } 

    [Fact] 
    public void Greeter_should_GreetBack_when_Greeted() 
    { 
     // set test actor as message sender 
     greeter.Tell(new Greet("John Snow"), TestActor); 
     // ExpectMsg tracks messages received by TestActors 
     ExpectMsg<GreetBack>(msg => msg.Who == "TestGreeter"); 
    } 

    [Fact] 
    public void Greeter_should_broadcast_incoming_greetings() 
    { 
     // create test probe and subscribe it to the event bus 
     var subscriber = CreateTestProbe(); 
     Sys.EventStream.Subscribe(subscriber.Ref, typeof (string)); 

     greeter.Tell(new Greet("John Snow"), TestActor); 

     // check if subscriber received a message 
     subscriber.ExpectMsg<string>("John Snow sends greetings"); 
    } 
} 

Jak widać, tutaj nie sprawdza stanu wewnętrznego aktora . Zamiast tego obserwuję, jak reaguje na sygnały wysyłane do niego i sprawdzam, czy jest to oczekiwany wynik.

+1

To ma wiele sensu, dziękuję. Tak więc jedynym sposobem na sprawdzenie, czy aktor jest w pewnym stanie Become, jest sprawdzenie, czy emituje określone typy wiadomości w tym stanie. – ardal

+2

Zasadniczo tak. Używając * nie komunikuj się przez dzielenie się, dzielenie przez komunikowanie * motta nie docierasz bezpośrednio do aktora (ponieważ konkretny aktor mój reinkarnuje się z powodu ewentualnej awarii lub może być umieszczony na innym komputerze i wszystko jest asynchroniczne). Możesz zweryfikować jego zachowanie, tak jak reaguje na nadchodzące sygnały. – Horusiath

4

Nie należy i nie należy tworzyć własnych ActorSystem w ramach żadnego testu Akka.TestKit. Możesz użyć wbudowanej właściwości Sys w dowolnym z testów, a uzyskasz dostęp do tego samego ActorSystem, który jest używany przez samą TestKit.

więc należy zrobić coś takiego:

var actor = Sys.ActorOf<BecomeActor>("become"); 

powód dlaczego jest to ważne: Mając TestActor i twój BecomeActor w tym samym ActorSystem jest konieczne, aby mogły wysyłać wiadomości do siebie , chyba że używasz Akka.Remote. W przeciwnym razie TestActor nie może odbierać żadnych wiadomości, a twoje rozmowy będą się kończyły.

EDYCJA: cały system aktorów jest teraz zbijany między testami jednostkowymi.

EDYCJA 2: Zobacz detailed guide we wrote to the Akka.NET TestKit for a longer explanation.

+0

To bardzo wyjaśnia, dziękuję. Inna sprawa: jaka jest różnica między niektórymi sposobami tworzenia aktora za pomocą TestKit? Takich jak: var actor = ActorOfAsTestActorRef ("powitanie"); var actor = Sys.ActorOf ("powitanie"); var actor = Sys.ActorOf (Props.Create (), "greeter") var actor = ActorOf (() => nowy Greeter(), "powitanie"); Czy wszystkie z nich będą skutkować tym samym aktorem? Czym się różnią? – ardal

+1

Wszystkie metody 'Sys.ActorOf' są równoważne. 'ActorOfAsTestActorRef' utworzy twojego aktora jako dziecko z" TestActor " – Aaronontheweb

+0

to komentarz o aktorów, którzy wciąż żyją pomiędzy testami, które wciąż są istotne @Aaronontheweb? Właśnie dostałem się do Akka.Net i TestKit, a najnowsza dokumentacja tutaj https://petabridge.com/blog/how-to-unit-test-akkadotnet-actors-akka-testkit/ sugeruje, że zostanie całkowicie usunięta i przebudowany dla każdego testu teraz – nrjohnstone