At present examples are sparse on unit testing SF actors, and I'm currently trying to get my head around what is the correct/best way to test the framework-provided features SF actors (state,reminders,timers etc).
One part of my brain (the way that I usually work) is telling me to unit test everything inside the actor seperately and integration test the boundaries of the actor. However I got a feeling that im going against the actor framework to do this.
For instance, for state management, I feel like I want to do something like as described in the 'New' method below and abstract-away the stateManager :
public class MyActor : Actor, IMyActor
{
private readonly IWidgetStore _widgetStore;
private readonly ISomeLogic _someLogic;
public MyActor(service, id, widgetStore, someLogic) : base(service, id){
_widgetStore = widgetStore;
}
public Task New(int x, int y){
var result = _someLogic.Calculate(x,y);
_widgetStore.Upsert(result);
}
public Task Old(int x, int y){
var result = _someLogic.Calculate(x,y);
this.actorStateManager.AddOrUpdateStateAsync("widget_state", result, (s, s1) => result).Wait();
}
}
public class WidgetStore : IWidgetStore
{
private readonly IActorStateManager _actorStateManager;
public WidgetStore(IActorStateManager actorStateManager)
{
_actorStateManager = actorStateManager;
}
public Task Upsert(string value)
{
_actorStateManager.AddOrUpdateStateAsync("widget_state", value, (s, s1) => value).Wait();
}
}
Doing this feels right, but I need a gaurantee that my widgetStore unit is actually working correctly and state is being persisted.
Two options
- Write a widgetStore unit test that verifies calls against the StateManager methods. Obviously this doesnt actually call the real implementation, but is the 'verify' suffice? I feel like I should be able to rely on the framework to actually do what its supposed to do... You as a consumer of the addition operator wouldn't write a test against the adddition operator to prove that 1 + 1 = 2 would you?
- Write a suite of tests that cover integrating against the real actor framework (with the runtime running on a local cluster). I will integrate against the real stateManager, and effectively black box test the Actors behavior using ActorProxy. If the behavior tests pass, then I must be storing state correctly? This feels better, but isn't this turning out to be an acceptance test?
As I draft this question, I feel like I should taking an approach to testing Actors which typically goes against how I have unit tested my services in the past.
I feel like I should be embracing actor framework & runtime as one holistic unit and test from a behavioral perspective, as described in Option 2.
I'm now looking to you, the SF community, for discourse and recommendations on this.
Edit
If I was to abstract the statemanager behind the widgetStore unit, there is no friendly way to get at the stateManager prior to Actor construction, so now I gotta do something like this (in the ActorRuntime.RegisterActorAsync method)...
Func<ActorService, ActorId, ActorBase> actorFactory = (service, id) =>
{
var widgetStore = new WidgetStore();
var someLogic = new SomeLogic();
var actor = new MyActor(service, id, widgetStore, someLogic);
widgetStore.Inject(actor.StateManager);
return actor;
};
This feels hacky as I now have to expose a method on the widgetStore to inject the stateManager, as you can't get at the stateManager prior to actor construction.