Ronald Wildenberg
Ronald Wildenberg

Reputation: 32094

Dependency injection of IActorStateManager in Azure Service Fabric stateful actor

For an Azure Service Fabric stateful service it is possible to inject the IReliableStateManager as follows:

ServiceRuntime.RegisterServiceAsync("MyServiceType", context =>
{
  IReliableStateManager stateManager = new ReliableStateManager(context);
  return new MyService(stateManager);
}

And this way you can mock IStateManager in unit tests for MyService.

The same doesn't seem possible for a stateful actor. IActorStateManager only has an internal implementation: Microsoft.ServiceFabric.Actors.Runtime.ActorStateManager. So how do I unit test a stateful actor?

At some point in my actor methods a call is made to IActorStateManager but since I can't inject this dependency, unit tests seem impossible.

Is there some way to work around this or is there another solution?

Upvotes: 4

Views: 863

Answers (2)

Yuriy Gavrishov
Yuriy Gavrishov

Reputation: 5021

I usually write actor business logic in separate class that have constructor with IStateManager parameter and implement my actor interface. Actor is just wrapper around actor implementation class and I test actorImpl class instead of actor. Look at the code:

public interface IMyActor01 : IActor
{
    Task<int> GetCountAsync();
    Task SetCountAsync(int count);
}

public class MyActor01Impl : IMyActor01
{
    private readonly IActorStateManager StateManager;

    public MyActor01Impl(IActorStateManager stateManager)
    {
        this.StateManager = stateManager;
        this.StateManager.TryAddStateAsync("count", 0);
    }

    public Task<int> GetCountAsync()
    {
        return this.StateManager.GetStateAsync<int>("count");
    }

    public Task SetCountAsync(int count)
    {
        return this.StateManager.AddOrUpdateStateAsync("count", count, (key, value) => count > value ? count : value);
    }
}

[StatePersistence(StatePersistence.Persisted)]
internal class MyActor01 : Actor, IMyActor01
{
    private MyActor01Impl Impl;

    protected override Task OnActivateAsync()
    {
        ActorEventSource.Current.ActorMessage(this, "Actor activated.");
        this.Impl = new MyActor01Impl(this.StateManager);
        return Task.FromResult(true);
    }

    Task<int> IMyActor01.GetCountAsync()
    {
        return this.Impl.GetCountAsync();
    }

    Task IMyActor01.SetCountAsync(int count)
    {
        return this.Impl.SetCountAsync(count);
    }
}

[TestFixture]
public class TestFixture01
{
    [Test]
    public void Test01()
    {
        //ARRANGE
        var dictionary = new Dictionary<string, object>();
        var impl = new MyActor01Impl(new StubStateManager(dictionary));

        //ACT
        impl.SetCountAsync(12).Wait();

        //ASSERT
        Assert.AreEquals(12, impl.GetCountAsync().Result);
        //or
        Assert.AreEquals(12, (int)dictionary["count"]);
    }
}

I can share StubStateManager implementation if you want.

Upvotes: 0

Vaclav Turecek
Vaclav Turecek

Reputation: 9050

No, IActorStateManager isn't injectable today, but we're working on making it so. For now (without using reflection) you'll have to wrap your state operations in something that you can inject, and have that call into the state manager, which you can then mock for unit testing.

Upvotes: 3

Related Questions