3
votes

I've started writing unit tests for new actor with state. The state is initialised in the OnActivateAsync method which is called by Service Fabric when the Actor is activated.

When unit testing, I'm creating the Actor myself and as the method is protected I don't have access from my unit test to call this method myself.

I'm wondering on the usual approach for this kind of testing. I could mock the Actor and mock the state, but for the code I want to test call the original. Am wondering if there is another approach I've not come across.

Another approach would be to move the State initialisation to somewhere else like a public method or in the constructor but the template for an Actor has the code there so it may be a best practice.

3

3 Answers

1
votes

I appreciate this is not ideal, but you can use reflection to call the OnActivateAsync() method.

For example,

var method = typeof(ActorBase).GetMethod("OnActivateAsync", BindingFlags.Instance | BindingFlags.NonPublic);
await (Task)method.Invoke(actor, null);

This way you'll be testing the actual method you want to test and also won't be exposing methods you don't really want to expose.

You may find it useful to group the creation of the actor and the manual call to OnActivateAsync() in a single method so that it's used across your test suite and it mimics the original Service Fabric behaviour.

4
votes

Use the latest version of ServiceFabric.Mocks NuGet package. It contains special extension to invoke OnActivateAsync protected method and the whole tool set for ServiceFabric unit testing.

        var svc = MockActorServiceFactory.CreateActorServiceForActor<MyActor>();
        var actor = svc.Activate(new ActorId(Guid.NewGuid()));
        actor.InvokeOnActivateAsync().Wait();
2
votes

I like to use the InternalsVisibleTo attribute and an internal method on the actor, which calls the OnActivateAsync method.

In the target Actor project, AssemblyInfo.cs add a line like this:

[assembly: InternalsVisibleTo("MyActor.Test")]

Where "MyActor.Test" is the name of the test project you want to grant access to your internal members.

In the target Actor class add a method something like this:

internal Task InvokeOnActivateAsync()
{
    return OnActivateAsync();
}

This way you can invoke the OnActivateAsync method from your test project something like this:

var actor = CreateNewActor(id);
actor.InvokeOnActivateAsync()