Akka.NET + DI + Testing Considerations

So at work we are using Akka.NET (The .NET port of the original Scala Akka. I prefer the Scala original but the .NET port is fairly good at keeping up to date with changes to the Scala original, so its ok) on a fairly big project, and one of the among the things we need to do are

 

  • Have different individuals/teams work on different Actor/several actors at once
  • Have rich supervision hierarchies
  • Use service abstractions to allow Mocking / Testing (you know take a dependency on ISomeService rather than SomeService directly, ah now we can feed in Mocks….lovely)
  • Have testing at the fore front of our minds
  • Allow our testers to test a single actor/or a portion of the Actor graph. The important thing is that the tester should be able to choose which single actor/portion of the entire system that they are testing, without needing the rest of the graph of the actor system

 

So we have had to come up with a way to build our actor system with all this in mind

 

So what is the riddle that we are trying to solve?

Well as I stated above we want to allow tests to feed in possible mocks or test doubles into our actors, but we also want to ensure that we can allows tests to create as much or as little of the overall actor system graph as the tester/test needs. So how do we go about doing that?

 

When you think about how Akka usually works it is usually done using a construct called Props, which is what summons our Actors into life. The Akka documentation says this about Props

 

Props is a configuration class to specify options for the creation of actors, think of it as an immutable and thus freely shareable recipe for creating an actor including associated deployment information (e.g. which dispatcher to use, see more below).

 

Here are some examples

 

Props props1 = Props.Create(typeof(MyActor));
Props props2 = Props.Create(() => new MyActorWithArgs("arg"));
Props props3 = Props.Create<MyActor>();
Props props4 = Props.Create(typeof(MyActorWithArgs), "arg");

 

It is a good idea to provide static factory methods on the ReceiveActor which help keeping the creation of suitable Props as close to the actor definition as possible

 

public class DemoActor : ReceiveActor
{
    private readonly int _magicNumber;

    public DemoActor(int magicNumber)
    {
        _magicNumber = magicNumber;
        Receive<int>(x =>
        {
            Sender.Tell(x + _magicNumber);
        });
    }

    public static Props Props(int magicNumber)
    {
        return Akka.Actor.Props.Create(() => new DemoActor(magicNumber));
    }
}

system.ActorOf(DemoActor.Props(42), "demo");

 

This idea of using Props also holds true for supervision, which means that a supervisor would use its own Context, where an example might look something like this

 

public class FirstActor : ReceiveActor
{
    IActorRef child = Context.ActorOf<MyActor>("myChild");
    // plus some behavior ...
}

 

Ok so what’s the issue with all of this, seems logical enough right?

  • We have some nice factory thing called Props that we can use to create our Actor. Heck we can even pass args to this thing, so we should be able to pass through what we need from tests too right
  • If we want to create a child actor we use our own Context and just use the child Props, and bingo we now have a child actor of the type we asked for under our supervision

 

All good right?

 

WRONG

 

While this all sounds cool, imagine these couple of simple scenarios with the above setup

 

  • You want to pass in real implementations of services in the real code, and you want to use test doubles/mocks in your tests. Ok Props does allow this, but its not very elegant
  • You DON’T want to use the real child actor,  but would like to use a test double actor instead. One where you can control the message flow using a well known test actor that allows you to test just the part of the overall system you want to test. You essentially want to replace part of the graph with your own logic, to allow you to focus your testing

 

This would not be that easy with the hard coded Props, and child actor creation. Is there anything we can do about this?

 

Well yes we could introduce a IOC container and we could try and come up with some weird way of creating Props from the IOC Container. Luckily this is already available in Akka.NET, as IOC is a fairly common requirement for testing these days. You can read more about this here : https://getakka.net/articles/actors/dependency-injection.html. There is support for the following IOC containers

  • Autofac (my personal container of choice)
  • Castle Windsor
  • NInject

 

The basic idea is that we can use the IOC container to create our props for us. The basic idea is something like this

// Create your DI container of preference
var someContainer = ... ;

// Create the actor system
var system = ActorSystem.Create("MySystem");

// Create the dependency resolver for the actor system
IDependencyResolver resolver = new XyzDependencyResolver(someContainer, system);

// Create the Props using the DI extension on your ActorSystem instance
var worker1Ref = system.ActorOf(system.DI().Props<TypedWorker>(), "Worker1");
var worker2Ref = system.ActorOf(system.DI().Props<TypedWorker>(), "Worker2");

 

We can even use this IOC integration to create child actors as follows

var myActorRef = Context.ActorOf(Context.DI().Props<MyActor>(), "myChildActor");

 

Does all this get us any closer to our goals? Well actually yes it does. The only niggle we need to fix now is how do allow a test to pick a different child actor to create instead of the real codes child actor. Remember we want to allow the tests to drive how much of the actor system is tested. So that (at least for me) means that the test should be responsible for saying what child actor is being created for a give test.

 

So can we have our cake an eat it too?

So can we have everything I have stated I want? Yes we can I have written some code which for me ticks all my boxes, and it obviously builds upon the DI integration that Akka.NET already provides, I am simply adding a bit more pixie dust into the mix to allow things to be truly testable

 

So where do I grab the code?

You can grab the code/tests here : https://github.com/sachabarber/AkkaNetDITesting

 

Lets walk through the various bits a pieces of the code at that repo

 

The Real Code

This section walks through the real code (which is a very simple actor system)

 

The Actors

I suppose the best place to start is to see the simple actor hierarchy that I am using for the demo app, where there are 2 simple actors

  • MySupervisorActor : Which simply creates a MyChildActor (through some child creation service that we will see soon) and sends it a BeginChildMessage. This actor also handles the following possible messages from the child actor that it creates
    • ChildSucceededMessage
    • ChildFailedMessage
  • MyChildActor : Which responds to the Sender with a ChildSucceededMessage

 

Lets see the code for these 2 actors

 

MySupervisorActor

using System;
using Akka.Actor;
using AkkaDITest.Messages;
using AkkaDITest.Services;

namespace AkkaDITest.Actors
{
    public  class MySupervisorActor : ReceiveActor
    {
        private readonly ISomeService _someService;
        private readonly IChildActorCreator _childActorCreator;
        private IActorRef originalSender;

        public MySupervisorActor(ISomeService someService, IChildActorCreator childActorCreator)
        {
            _someService = someService;
            _childActorCreator = childActorCreator;
            Receive<StartMessage>(message =>
            {
                originalSender = Sender;
                var x = _someService.ReturnValue("war is a big business");
                Console.WriteLine($"ISomeService.ReturnValue(\"war is a big business\") gave result {x}");

				
				//IF WE WANT TO USE BACKOFF THIS IS HOW WE WOULD DO IT

                //var supervisor = BackoffSupervisor.Props(
                //    Backoff.OnFailure(
                //        _childActorCreator.GetChild(ActorNames.MyChildActorName,Context),
                //        childName: ActorNames.MyChildActorName,
                //        minBackoff: TimeSpan.FromSeconds(3),
                //        maxBackoff: TimeSpan.FromSeconds(30),
                //        randomFactor: 0.2));
                //return ctx.ActorOf(supervisor);
				
                var childActor = Context.ActorOf(_childActorCreator.GetChild(ActorNames.MyChildActorName,Context), ActorNames.MyChildActorName);
                childActor.Tell(new BeginChildMessage());

            });
            Receive<ChildSucceededMessage>(message =>
            {

                Console.WriteLine($"{message.FromWho}_ChildSucceededMessage");
                originalSender.Tell(message);
            });
            Receive<ChildFailedMessage>(message =>
            {
                Console.WriteLine($"{message.FromWho}_ChildFailedMessage");
                originalSender.Tell(message);
            });
        }
    }
}

 

See how we don’t create the child actor ourselves but rely on a service called IChildActorCreator which is responsible for giving back Props for the chosen actor. This way we can provide a test version of this service which will provide Props for alternative test double child actors that may be used to steer the code in whichever manor the tests require

 

MyChildActor

using System;
using Akka.Actor;
using AkkaDITest.Messages;
using AkkaDITest.Services;

namespace AkkaDITest.Actors
{
    public  class MyChildActor : ReceiveActor
    {
        private IFooService _fooService;


        public MyChildActor(IFooService fooService)
        {
            _fooService = fooService;

            Receive<BeginChildMessage>(message =>
            {
                var x = _fooService.ReturnValue(12);
                Console.WriteLine($"IFooService.ReturnValue(12) gave result {x}");

                Sender.Tell(new ChildSucceededMessage("MyChildActor"));
            });
        }
    }
}

 

Lets also have a quick look at that special Props creation service.

 

Real ChildActorCreator service

Here is the real code version, when we are in tests we will use a different version of this service, which will inject into the IOC container to override this original code one.  You can see below that we use the Akka.NET DI feature to resolve the Props for the new actor that we want to get Props for. This means all the dependencies that that Props needs from the IOC container will be satisfied for us using our chosen container (proving we have the relevant Nuget for that container of choice installed)

 

using System;
using System.Collections.Generic;
using System.Text;
using Akka.Actor;
using Akka.DI.Core;
using AkkaDITest.Messages;

namespace AkkaDITest.Actors
{

    public interface IChildActorCreator
    {
        Props GetChild(string actorNameKey, IUntypedActorContext context);
    }


    public class ChildActorCreator : IChildActorCreator
    {
        private Dictionary<string, Func<IUntypedActorContext, Props>> _propLookup =
            new Dictionary<string, Func<IUntypedActorContext, Props>>();

        public ChildActorCreator()
        {
            _propLookup.Add(ActorNames.MyChildActorName, (context) => context.DI().Props<MyChildActor>());
        }

        public Props GetChild(string actorNameKey, IUntypedActorContext context)
        {
            return _propLookup[actorNameKey](context);
        }

        public string Name => "ChildActorCreator";

    }
}

 

You might be asking why stop at Props and not have this service create a new IActorRef, well there is good reason for that. Akka.NET comes with powerful pre-canned backoff supervision strategies which work by using Props not IActorRef so we want to leave that avenue open to us should we wish to use it.

 

Default IOC Registration

The final pieces of the puzzle to making the real code work are the IOC container wireup and the initial MySupervisorActor instantiation. Both of these are shown below.

 

Here is the Autofac wireup (remember if you choose to use a different container your code will look different to this)

 

using System;
using Autofac;

namespace AkkaDITest.IOC
{
    public class ContainerOperations
    {
        private static readonly Lazy<ContainerOperations> _instance = new Lazy<ContainerOperations>(() => new ContainerOperations());
        private IContainer localContainer;
        private object _syncLock = new Object();

        private ContainerOperations()
        {
            CreateContainer();
        }

        public void ReInitialise()
        {
            lock (_syncLock)
            {
                localContainer = null;
            }
        }


        public static ContainerOperations Instance => _instance.Value;


        public IContainer Container
        {
            get
            {
                lock (_syncLock)
                {
                    if (localContainer == null)
                    {
                        CreateContainer();

                    }
                    return localContainer;
                }
            }
        }

        private void CreateContainer()
        {
            var builder = new ContainerBuilder();
            builder.RegisterModule(new GlobalAutofacModule());


            AddExtraModulesCallBack?.Invoke(builder); 

            localContainer = builder.Build();

        }

        public Action<ContainerBuilder> AddExtraModulesCallBack { get; set; } 
    }
}

 

And this is the main Autofac module for my initial runtime registrations

using Akka.Actor;
using AkkaDITest.Actors;
using AkkaDITest.Services;
using Autofac;
using System;

namespace AkkaDITest.IOC
{
    public class GlobalAutofacModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<SomeService>()
                .As<ISomeService>()
                .SingleInstance();

            builder.RegisterType<FooService>()
                .As<IFooService>()
                .SingleInstance();

            builder.RegisterType<ChildActorCreator>()
                .As<IChildActorCreator>()
                .SingleInstance();

            builder.RegisterType<MySupervisorActor>();
            builder.RegisterType<MyChildActor>();

            var _runModelActorSystem = new Lazy<ActorSystem>(() =>
            {
                return ActorSystem.Create("MySystem");
            });

            builder.Register<ActorSystem>(cont => _runModelActorSystem.Value);


        }
    }
}

 

There are a couple of points to note here:

 

  • That the ActorSystem itself is registered in the IOC container
  • That each actor we want to resolve Props for using the IOC container is also registered in the IOC container
  • That all injectable args for the actor Props are also registered in the IOC container
  • That within the ContainerOperations class we allow the container to be ReInitialised (we found this is very useful in testing)
  • That we allow tests to provide more modules which will serve as overrides for any already registered components. Again this is a great aid in testing as we shall see soon

 

So with all that in place, we should now be able to turn our attention to the testing aspect of our simple demo app.

 

 

The Test Code

This section walks through the test code (which is simply testing the demo app codebase)

 

Just A Reminder

So lets just remind ourselves of what we are trying to achieve in our test code

  • We would like to be able to test any actor by itself should we wish to (think unit tests)
  • We would also like to be able to provide mock services should we wish to (think unit tests)
  • We should also be able to test the entire actor graph using the real services should we wish to (think integration tests)

 

Does what I have covered gives us all of this?

YES I think so, lets see how

 

So lets say we want to test the demo apps MySupervisorActor which in the real code creates a child actor “MyChildActor” which we are expecting to do the real work, and respond with it own messages. Now lets say I want to use my own version of that “MyChildActor” one that I can use in my tests, to drive the behavior of the MySupervisorActor, this is now relatively easy using a test version of the IChildCreator service that instead of creating Props for the REAL MyChildActor will create Props for a test double of my choosing.

 

I have chosen to do this using a mocking framework (I am using Moq here)

Mock<IChildActorCreator> mockChildActorCreator = new Mock<IChildActorCreator>();
mockChildActorCreator.Setup(x => x.GetChild(ActorNames.MyChildActorName, It.IsAny<IUntypedActorContext>()))
    .Returns((string childName, IUntypedActorContext context) => context.DI().Props<TestChildActor>());

 

This allows me to create any child Props for the actor under test of my choosing using the test code. In this example we will be using this test double child actor instead of the real codes MyChildActor

 

using System;
using Akka.Actor;
using AkkaDITest.Messages;
using AkkaDITest.Services;

namespace AkkaDITest.Tests.Actors
{
    public  class TestChildActor : ReceiveActor
    {
        private IFooService _fooService;


        public TestChildActor(IFooService fooService)
        {
            _fooService = fooService;

            Receive<BeginChildMessage>(message =>
            {
                var x = _fooService.ReturnValue(12);
                Console.WriteLine($"IFooService.ReturnValue(12) gave result {x}");

                Sender.Tell(new ChildSucceededMessage("TestChildActor"));
            });
        }
    }
}

 

This test double actor will respond in the way I see fit to drive my tests. Obviously this example is very simplistic and could be expanded upon, and I could have multiple test double actors, or even have a test double actor which allowed me to set the return message type I wanted for my tests.

 

So now that we have the ability to use test double Props and have a way of plumbing that into the overall actor graph that we want to test, lets see an actual test

 

For me a simple test for the MySupervisorActor using this test double TestChildActor looks like this

 

using System;
using Akka.Actor;
using Akka.DI.AutoFac;
using Akka.DI.Core;
using Akka.TestKit.NUnit3;
using AkkaDITest.Actors;
using AkkaDITest.IOC;
using AkkaDITest.Messages;
using AkkaDITest.Services;
using AkkaDITest.Tests.Actors;
using Autofac;
using Moq;
using NUnit.Framework;
using NUnit.Framework.Internal;

namespace AkkaDITest.Tests
{
    [TestFixture]
    public class MySupervisorActorTests : TestKit
    {
        [SetUp]
        public void SetUp()
        {
            ContainerOperations.Instance.ReInitialise();
        }

        [Test]
        public void Correct_Message_Received_When_Using_TestChildActor_Test()
        {
            Mock<IChildActorCreator> mockChildActorCreator = new Mock<IChildActorCreator>();
            mockChildActorCreator.Setup(x => x.GetChild(ActorNames.MyChildActorName, It.IsAny<IUntypedActorContext>()))
                .Returns((string childName, IUntypedActorContext context) => context.DI().Props<TestChildActor>());

            //Setup stuff for this testcase
            Mock<ISomeService> mockSomeService = new Mock<ISomeService>();
            mockSomeService.Setup(x => x.ReturnValue(It.IsAny<string>())).Returns("In a test mock");
            ContainerOperations.Instance.AddExtraModulesCallBack = builder =>
            {
                builder.Register(x=> mockSomeService.Object)
                    .As<ISomeService>()
                    .SingleInstance();

                builder.Register(x => mockChildActorCreator.Object)
                  .As<IChildActorCreator>()
                  .SingleInstance();

                builder.RegisterType<TestChildActor>();

            };

            var system = ContainerOperations.Instance.Container.Resolve<ActorSystem>();
            IDependencyResolver resolver = new AutoFacDependencyResolver(ContainerOperations.Instance.Container, system);
            var mySupervisorActor = system.ActorOf(system.DI().Props<MySupervisorActor>(), "MySupervisorActor");
            mySupervisorActor.Tell(new StartMessage(), TestActor);


            // Assert
            AwaitCondition(() => HasMessages, TimeSpan.FromSeconds(10));
            var message = ExpectMsg<ChildSucceededMessage>();
            Assert.AreEqual("TestChildActor", message.FromWho);
        }
    }
}

 

There are a couple of points to note here

  • We are using the Akka.NET TestKit base class which is very useful for testing actors
  • We providing a container level module overrides where we override these initial services with our test versions of them
    • ISomeService
    • IChildCreator
  • We make use of some of the Akka.NET TestKit goodies such as
    • TestActor (An IActorRef that we can use as a Sender)
    • AwaitCondition(…)
    • ExpectMsg(…)

 

Conclusion

Anyway there you go, I hope this small post shows you that it is still possible to have rich actor hierarchies and to still be able to break it down and test it in chunks and force the code in the desired paths using test doubles/mocks/fakes

 

Enjoy

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s