I'm working on an Akka.net system using .net core, and writing a feature where one actor executes a process every 24 hours using the Scheduler.
I'd like to unit test this, but of course the test needs to run quickly. I'm trying to use the Akka.TestKit TestScheduler to artificially advance time, but examples seem hard to come by. My code works with the standard Scheduler, but I can't get the TestScheduler to work.
I've distilled my process down to a simple actor and 2 unit tests (see code below). One is a test that runs successfully with the standard scheduler. The other runs with the TestScheduler, but fails. The actor isn't receiving messages from the TestScheduler and I'm not sure what I might be doing wrong or how to debug this further.
Any help would be appreciated.
using System;
using System.Linq;
using Akka.Actor;
using Akka.TestKit;
using Akka.TestKit.Xunit2;
using Xunit;
namespace akkaSchedulerTest.Test
{
// Requires Nuget Package "Akka.TestKit.Xunit2" (version 1.3.11 at the time this was written)
public class RepeatedScheduleTest_Standard_Scheduler : TestKit
{
/// <summary>
/// This test passes
/// </summary>
[Fact]
public void Sender_Actor_Sends_Out_Message_Expected_Number_Of_Times()
{
var interval = TimeSpan.FromSeconds(1);
var testDuration = TimeSpan.FromSeconds(2.1);
int expectedMessageCount = 2;
var recipientActorRef = this.TestActor;
var messageText = "Message";
var senderActor = Sys.ActorOf(Props.Create(() => new RepeatedMessageSenderActor(recipientActorRef, messageText, interval)));
// verify that the other actor receives the right number of messages over the expected time
var allExpectedMessages = Enumerable.Repeat(messageText, expectedMessageCount).ToArray();
ExpectMsgAllOf(testDuration, allExpectedMessages);
}
}
public class RepeatedScheduleTest_Test_Scheduler : TestKit
{
// REF: https://petabridge.com/blog/how-to-unit-test-akkadotnet-actors-akka-testkit/#how-do-i-test-scheduled-messages
public RepeatedScheduleTest_Test_Scheduler()
: base(@"akka.scheduler.implementation = ""Akka.TestKit.TestScheduler, Akka.TestKit""") { }
private TestScheduler Scheduler => (TestScheduler)Sys.Scheduler;
/// <summary>
/// This test fails with the error:
/// Timeout (00:00:03) while expecting 5 messages. Only got 0 after 00:00:02.9992044.
/// In realtime it should last many hours. This is an attempt to verify the scheduled behavior in a quick test.
/// </summary>
[Fact]
public void Sender_Actor_Sends_Out_Message_Expected_Number_Of_Times()
{
var interval = TimeSpan.FromHours(1);
var testDuration = TimeSpan.FromHours(5.1);
int expectedMessageCount = 5;
var recipientActorRef = this.TestActor;
var messageText = "Message";
var senderActor = Sys.ActorOf(Props
.Create(() => new RepeatedMessageSenderActor(recipientActorRef, messageText, interval))
.WithDispatcher(CallingThreadDispatcher.Id));
// advance the TestScheduler by enough time that the repeated messages should be processed
Scheduler.Advance(testDuration);
// verify that the other actor receives the right number of messages over the expected time
var allExpectedMessages = Enumerable.Repeat(messageText, expectedMessageCount).ToArray();
ExpectMsgAllOf(allExpectedMessages);
}
}
public class RepeatedMessageSenderActor : ReceiveActor
{
private readonly IActorRef _recipientActorRef;
private readonly string _messageText;
private readonly TimeSpan _interval;
public RepeatedMessageSenderActor(IActorRef recipientActorRef, string messageText, TimeSpan interval)
{
_recipientActorRef = recipientActorRef;
_messageText = messageText;
_interval = interval;
Receive<SendOutMessage>(msg => HandleSendOutMessage());
// use the scheduler to repeatedly tell this actor to send out a message at a regular interval
Context.System.Scheduler
.ScheduleTellRepeatedly(_interval, _interval, Self, new SendOutMessage(), Self);
}
/// <summary>
/// in the failing test, this method never gets called
/// </summary>
private void HandleSendOutMessage()
{
_recipientActorRef.Tell(_messageText);
}
public class SendOutMessage { }
}
}