In Getting Started with Akka.NET and Handling messages and state with Akka.NET we created a calculator in Akka.NET that was able to add and subtract numbers and return the answer. It also stores the last answer and can respond with it when asked.
In this post we’ll introduce TestKit which will enable us to write unit and integration tests.
Code for this post: https://github.com/HCanber/akka.net-blog-examples/tree/master/03-calculator-testkit-unit
All posts in this series: Tutorials for Akka.NET
Note! This post was written using Akka.NET 0.7.0 and might not work for later versions
TestKit
TestKit is a module that allows us to write two types of tests:
- Testing isolated pieces of code without involving the actor model, meaning without multiple threads; this implies completely deterministic behavior concerning the ordering of events and no concurrency concerns and will be called Unit Testing in the following.
- Testing (multiple) encapsulated actors including multi-threaded scheduling; this implies non-deterministic order of events but shielding from concurrency concerns by the actor model and will be called Integration Testing in the following.
In Akka.NET TestKit is distributed in two nuget packages. First you need Akka.TestKit
which contains all base functionality and then you need one targeted for a specific Test framework. At the time of writing, we have nuget packages for two frameworks: xUnit and VSTest/MSTest: Akka.TestKit.Xunit
and Akka.TestKit.VsTest
Add a test project
We’ll continue with the code from Handling messages and state with Akka.NET which can be found on GitHub by adding a xUnit test project. The result can also be found on GitHub.
Start by adding a new Class Library project to the solution. We’ll call it Calculator.Tests
. Delete Class1.cs
from the project.
Add Akka.TestKit.XUnit to the project
Add the package Akka.TestKit.XUnit
to the test project
Install-Package Akka.TestKit.Xunit
This will install all packages needed (Akka
, Akka.TestKit
, xunit
).
Add a reference to the console app project.
Create the first test class
Create a new class, inherit from TestKit
using Akka.TestKit.Xunit;
using CalculatorApp;
using Xunit;
namespace Calculator.Tests
{
public class CalculatorUnitTests : TestKit
{
}
}
Now we’re good to go!
TestKit’s test system
When writing tests using TestKit (by inheriting from TestKit
) it creates an ActorSystem
for us in which the tests are run, so you do not need to create one yourself.
The system can be accessed using Sys
. This means that to create an actor in this system you’d do:
var calculator = Sys.ActorOf<CalculatorActor>("calculator");
Since this is done so frequently in tests you can skip the Sys
property:
var calculator = ActorOf<CalculatorActor>("calculator");
Unit testing actors using TestActorRef
Unit testing actors is when we have full control over the order messages are processed, no concurrency and everything is synchronous. TestKit also provides a way to actually verify internal state of an actor.
Normally, when we create an actor like this we don’t get access to it’s internal state since the proxy ActorRef
is returned:
ActorRef calculator = system.ActorOf<CalculatorActor>("calculator");
When using TestKit there is an overload that returns a TestActorRef<TActor>
which gives as access to the underlying actor instance via the property UnderlyingActor
.
The first test: Internal state answer should be 0 initially
Right after a calculator has been created it’s internal stored answer should be 0. To verify this is true, we need to make some changes to CalculatorActor
so we can access the answer
value. The local variable answer
is turned into a private field, and we add an Answer
property:
public class CalculatorActor : ReceiveActor
{
private double _answer;
public CalculatorActor()
{
Receive<Add>(add =>
{
_answer = add.Term1 + add.Term2;
Sender.Tell(new Answer(_answer));
});
Receive<Subtract>(sub =>
{
_answer = sub.Term1 - sub.Term2;
Sender.Tell(new Answer(_answer));
});
Receive<GetLastAnswer>(m => Sender.Tell(new Answer(_answer)));
}
public double Answer { get { return _answer; } }
}
Now we can write the test:
[Fact]
public void Answer_should_initially_be_0()
{
TestActorRef<CalculatorActor> calculatorRef = ActorOfAsTestActorRef<CalculatorActor>("calculator");
CalculatorActor calculator = calculatorRef.UnderlyingActor;
Assert.Equal(0, calculator.Answer);
}
Instead of ActorOf<CalculatorActor>
we use ActorOfAsTestActorRef<CalculatorActor>
which returns TestActorRef<CalculatorActor>
. This type exposes the underlying actor in the UnderlyingActor
property.
Once we got hold of it we verify that the Answer
is 0.
If we run this test it passes.
Verifying that state is modified after handling messages
The CalculatorActor
should always store the latest answer. So of we send it Add(1,1)
the Answer
property should be 2.
[Fact]
public void After_adding_1_and_1_Answer_should_be_2()
{
TestActorRef<CalculatorActor> calculatorRef = ActorOfAsTestActorRef<CalculatorActor>("calculator");
calculatorRef.Tell(new Add(1,1));
CalculatorActor calculator = calculatorRef.UnderlyingActor;
Assert.Equal(2, calculator.Answer);
}
Remember that everything is synchronous when writing these kind of tests. It’s the use of
ActorOfAsTestActorRef<T>()
that makes it synchronous. ACallingThreadDispatcher
is used for actors created usingActorOfAsTestActorRef<T>()
so when we send it a message usingTell(message)
it’s not dispatch on another thread, but immediately processed beforeTell
returns control back to our test.
Code
Code on GitHub: https://github.com/HCanber/akka.net-blog-examples/tree/master/03-calculator-testkit-unit
All posts in this series: Tutorials for Akka.NET
No comments:
Post a Comment