如何在 Akka.NET 中使用 TestKit

How to use TestKit in Akka.NET

我正在尝试测试我的 Akka.NET 演员,但在使用 TestKit 和理解它的工作原理时遇到了一些问题。

由于 Akka.NET 中还没有官方的单元测试文档,我已经探索了 Akka.NET 代码库的示例代码,但是那里使用的示例对我不起作用。

我用作参考的测试是 ReceiveActorTests.cs and ReceiveActorTests_Become.cs,因为它们与我要在我的应用中测试的场景很接近。

这是一些虚拟代码:

鉴于此演员

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

我想测试它是否正确接收了问候和告别消息,并正确进入了 Become-states。查看 ReceiveActorTests_Become.cs 测试,一个 actor 是由

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

并且

发送并断言了一条消息
actor.Tell(message, TestActor);
ExpectMsg(message);

但是,当我尝试使用这种方法来实例化一个 actor 以及许多其他基于 TestKit 方法(见下文)的方法时,我不断收到相同的测试失败错误:

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

这是我的测试:

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

我也试过将 ExpectMsg 行移动到 actor.Tell 行上方(因为在你对它采取行动之前进行预期更有意义,而不是在之后验证预期),但这也会导致超时错误。

我尝试过使用 NUnit 和 XUnit TestKits。

我可能忽略了一些非常基本的东西。

您不会也不应该创建自己的 ActorSystem 作为任何 Akka.TestKit 测试的一部分。您可以在任何测试中使用内置的 Sys 属性,您将可以访问 TestKit 本身使用的相同 ActorSystem

所以你应该这样做:

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

这很重要的原因:让 TestActor 和你的 BecomeActor 在同一个 ActorSystem 中是为了它们相互发送消息,除非您使用的是 Akka.Remote。否则 TestActor 无法接收任何消息并且您的 ExpectMsg 呼叫将超时。

编辑:整个测试参与者系统现在在单元测试之间被拆除。

编辑 2:参见 detailed guide we wrote to the Akka.NET TestKit for a longer explanation

TestKit 用于更多的行为测试,以验证您的 actor 在整个 actor 系统的上下文中是否按预期工作。这更像是黑盒测试——您不会直接触及演员的内部。相反,最好关注 给定信号 A 和参与者行为 B 之类的行为,它应该向另一个参与者 D.

发出消息 C

在您的示例中,Greeter actor 的问题在于它是静音的 - 虽然它可以接收一些输入,但它不会做任何结果。从整个系统的角度来看,它可能已经死了,没人会在乎。

使用其他示例 - 给定以下演员:

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

让我们创建一个示例测试规范:

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");
    }
}

如你所见,这里我不检查actor的内部状态。相反,我正在观察它如何对我发送给它的信号做出反应,并验证它是否是预期结果。