Reputation: 32094
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
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
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