c# - How to use TestKit to not process messages sent to Self
问题描述
I'm trying to test an Actor. On one scenario I want to focus on how it processes a message. For various design reasons, I want to split up an action in to 2 separate distinct steps / messages for the same actor. Mainly, it is possible that the 2nd stage (i.e. the second message) will not be able to be processed and may have to be rescheduled. So for my unit tests, I want to focus on the stage 1 behavior, but using Test Kit it automatically processes the message, because I call this this.Self.Tell("2");
.
I've simplified the code and put it here. The following is the actor code:
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;
}
}
}
Here is the test method
public void TestSendingMessage()
{
target = Sys.ActorOf(Props.Create(() => new TestSelfActor()));
target.Tell("1");
actual = this.ExpectMsg<string>();
Assert.AreEqual("test", actual);
}
If I run this, it will pass, and the output will show
one
two
Ideally, there is some way to setup the test so I can send it the first message and then assert that it told itself the message "2", without it every outputting to the console "2".
Some extra info, I'm running .NET Framework 4.8, and akka/akka.testkit 1.3.8, although I think the code and problem are simple enough that it doesn't matter the version?
解决方案
In your test project you can inherit from TestSelfActor and override AroundReceive method (then you can fake it with NSubstitute, Moq, ...), In AroundReceive you can intercept messages and decide how to handle them (e.g. return false for "2" in your case) Below is example using XUnit and NSubstitute. I could also extend it and create fake MessageHandler in XUnit fixture to make it reusable. You can always pass null if you want to test default behavior. Hope this helps
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");
}
}
}
推荐阅读
- laravel - Laravel Calendar.io 使用控制器显示视图
- jenkins - 如何使用 Jenkins 和 openshift 定义 BuildConfig 对象
- ios - 来自 Calendar.dateComponents 的日期在 Swift 中返回 nil
- javascript - 交互式更新在热图中不起作用
- python - PySide2 (5.11.x):无法在 Python 2.7 中调用 type(QtWidgets.QWidget).__new__
- wordpress - Wordpress 外观 > 定制器停止工作
- r - 将时间测量转换为 R 中的变量
- php - MongoDB PHP驱动程序:限制选项无效
- sql - postgresql - 具有多个表的列的分页/排序
- python - Python OpenCV 错误:当前线程不是对象的线程