dapr / dotnet-sdk

Dapr SDK for .NET
Apache License 2.0
1.12k stars 340 forks source link

Documentation on writing unit tests for Actors that access state #1169

Open jaq316 opened 1 year ago

jaq316 commented 1 year ago

Documentation on writing unit tests, specifically for Actors accessing state would be great.

The below code, for instance, would throw: "InvalidOperationException: The actor was initialized without a state provider, and so cannot interact with state. If this is inside a unit test, replace Actor.StateProvider with a mock."

    internal class MyActor : Actor, IMyActor
    {

        private const string STATE_KEY = "counter";

        public MyActor(ActorHost host) // Accept ActorHost in the constructor
            : base(host) // Pass ActorHost to the base class constructor
        {
        }

        public async Task<int> GetCounter()
        {
            var iCounterState  =(await StateManager.TryGetStateAsync<int>(STATE_KEY).ConfigureAwait(false));
            return iCounterState.Value;
        }
        public async Task Increment()
        {
            var iCounterState = (await StateManager.TryGetStateAsync<int>(STATE_KEY).ConfigureAwait(false));
            if (iCounterState.HasValue)
            {
                await StateManager.SetStateAsync(STATE_KEY, iCounterState.Value + 1).ConfigureAwait(false);
            }
            else
            {
                await StateManager.SetStateAsync(STATE_KEY, 1).ConfigureAwait(false);
            }   

        }

    }

    public class MyActorTests
    {
        [Fact]
        public async Task SomeMethod_ShouldSucceed()
        {
            // Arrange
            var actorId = new ActorId("test");
            var host = ActorHost.CreateForTest<MyActor>(new ActorTestOptions
            {
                ActorId = actorId
            });

            var myActor = new MyActor(host);

            // Act
            await myActor.Increment();

            var counter = myActor.GetCounter();
            // Assert
            Assert.AreEqual(1, counter);

        }
    }
jaq316 commented 1 year ago

https://github.com/dapr/dotnet-sdk/blob/3b979e6bdb1d779563f1656fa684183b2bfecd08/test/Dapr.Actors.Test/ITestActor.cs#L47C31-L47C31 implements overriding of the StateManager by providing an optional IStateManager parameter in the constructor. If that is provided, this.StateManager is set in the constructor.

paule96 commented 3 months ago

@jaq316 is this a recommendation to provide an IActorStartManager parameter in every actor, so it is testable? That sounds kinda strange because in the real world the Actors doesn't need this injecten