如何使用 TestKit 不处理发送给自己的消息

How to use TestKit to not process messages sent to Self

我正在尝试测试 Actor。在一种情况下,我想关注它如何处理消息。出于各种设计原因,我想将一个动作拆分为同一演员的 2 个独立的不同步骤/消息。主要是,第二阶段(即第二条消息)可能无法处理,可能需要重新安排。所以对于我的单元测试,我想关注第 1 阶段的行为,但是使用 Test Kit 它会自动处理消息,因为我称之为 this.Self.Tell("2");.

我简化了代码并将其放在这里。以下是演员代码:

public class TestSelfActor : UntypedActor
{
    protected override void OnReceive(object message)
    {
        switch (message)
        {
            case string s:
                if (s == "1")
                {
                    Console.WriteLine("one");
                    this.Self.Tell("2");
                    this.Sender.Tell("test");
                }
                else if (s == "2") { Console.WriteLine("two"); }
                break;
        }
    }
}

这里是测试方法

public void TestSendingMessage()
{
    target = Sys.ActorOf(Props.Create(() => new TestSelfActor()));
    target.Tell("1");

    actual = this.ExpectMsg<string>();
    Assert.AreEqual("test", actual);
}

如果我运行这个,它会通过,输出会显示

one
two

理想情况下,有一些方法可以设置测试,这样我就可以向它发送第一条消息,然后 然后 断言它告诉自己消息“2”,而不是每次输出到控制台“2”。

一些额外的信息,我正在 运行ning .NET Framework 4.8 和 akka/akka.testkit 1.3.8,虽然我认为代码和问题足够简单,但它并不版本有关系吗?

在您的测试项目中,您可以继承自 TestSelfActor 并覆盖 AroundReceive 方法(然后您可以使用 NSubstitute、Moq 等来伪造它), 在 AroundReceive 中,您可以拦截消息并决定如何处理它们(例如,return false for "2" in your case) 下面是使用 XUnit 和 NSubstitute 的例子。我还可以扩展它并在 XUnit fixture 中创建伪造的 MessageHandler 以使其可重用。如果你想测试默认行为,你总是可以传递 null 。 希望这有帮助

using Akka.Actor;
using Akka.TestKit.Xunit;
using NSubstitute;
using System;
using Xunit;

namespace ActorTests
{
    public class TestSelfActor : UntypedActor
    {
        protected override void OnReceive(object message)
        {
            switch (message)
            {
                case string s:
                    if (s == "1")
                    {
                        Console.WriteLine("one");
                        this.Self.Tell("2");
                        this.Sender.Tell("test");
                    }
                    else if (s == "2") { Console.WriteLine("two"); }
                    break;
            }
        }
    }

    public interface IMessageHandler
    {
        Func<object, bool> AroundReceiveHandler { get; }
    }

    public class FakeActor : TestSelfActor
    {
        private readonly IMessageHandler _messageHandler;

        public FakeActor(IMessageHandler messageHandler)
        {
            _messageHandler = messageHandler;
        }
        protected override bool AroundReceive(Receive receive, object message)
        {
            var acceptMessage = _messageHandler?.AroundReceiveHandler?.Invoke(message) ?? true;
            return acceptMessage && base.AroundReceive(receive, message);
        }
    }

    public class SelfActorSpecs : TestKit
    {
        [Fact]
        public void When_Sender_TellsOne_Actor_Should_RespondWith_Test_NotSending_Two_ToSelf()
        {
            var messageHandler = Substitute.For<IMessageHandler>();
            messageHandler.AroundReceiveHandler(Arg.Any<string>()).Returns(true);
            messageHandler.AroundReceiveHandler("2").Returns(false);

            var target = Sys.ActorOf(Props.Create(() => new FakeActor(messageHandler)));
            target.Tell("1");
            ExpectMsg<string>(x => x == "test");
        }
    }
}