This tutorial shows you how to write unit tests for your grains to make sure they are behaving correctly. For a distributed application written in Orleans, you’ll need load tests and integration tests as well but here we’ll only focus on unit tests.
Orleans makes it possible to mock many of its parts (example) but here we only focus on simply running grains in test silos.
The steps are
Orleans.TestingHost.TestingSiloHost.The TestingSiloHost creates a mini cluster of 2 silos in 2 different AppDomains and initializes a client in the main AppDomain which test cases will run on, in its constructor. Then it will run all of the test cases in the class and at cleanup time shuts the silos down. The test cases like ordinary grain code should call grains and then wait for the results using await and should not block the execution.
The TestingSiloHost which we inherit from starts the silos up for us but we need to shutdown them ourselves.
The samples here are using MS Test but you can use NUnit, XUnit or any other testing framework that you want. Let’s use the hello world sample’s code here as an example.
using System;
using System.Threading.Tasks;
using HelloWorldInterfaces;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Orleans;
using Orleans.TestingHost;
namespace Tests
{
[TestClass]
public class HelloWorldSiloTests : TestingSiloHost
{
[ClassCleanup]
public static void ClassCleanup()
{
// Optional.
// By default, the next test class which uses TestingSiloHost will
// cause a fresh Orleans silo environment to be created.
StopAllSilos();
}
[TestMethod]
public async Task SayHelloTest()
{
// The Orleans silo / client test environment is already set up at this point.
const long id = 0;
const string greeting = "Bonjour";
IHello grain = GrainFactory.GetGrain<IHello>(id);
// This will create and call a Hello grain with specified 'id' in one of the test silos.
string reply = await grain.SayHello(greeting);
Assert.IsNotNull(reply, "Grain replied with some message");
string expected = string.Format("You said: '{0}', I say: Hello!", greeting);
Assert.AreEqual(expected, reply, "Grain replied with expected message");
}
}
}
Since this test method is asynchronous and leverages the await keyword, be sure the method is defined as async and returns a Task.
As you can see having ClassCleanup() is optional but it’s good to have it around.
Our test method simply creates a grain, sends the message to it and then first checks if the result is null or not and then checks if it is in the expected format or not.
So writing tests for Orleans is not much different from a normal test project.
You just need to reference the two NuGet packages and derive your class from TestingSiloHost.
TestingSiloHost class has multiple utility methods for starting and stopping silos which can be used for programmatic simulation of some failure situations.MembershipTableGrain system grain, to allow bootstrap. In production there is no primary and secondary silos: all silos are equal and behave in a peer-to-peer fashion.TestSiloOptions and TestClientOptions objects which can be passed to the constructor of the base class.