OrleansContrib / OrleansTestKit

Unit Test Toolkit for Microsoft Orleans
http://dotnet.github.io/orleans
MIT License
78 stars 43 forks source link

Question for this.AsReference<IMyGrain> in timers #122

Closed amccool closed 9 months ago

amccool commented 2 years ago

getting the following when testing a timer

System.ArgumentException : Passing a half baked grain as an argument. It is possible that you instantiated a grain class explicitly, as a regular object and not via Orleans runtime or via proper test mocking (Parameter 'grain')

  Stack Trace: 
GrainExtensions.AsWeaklyTypedReference(IAddressable grain)
GrainExtensions.AsReference[TGrainInterface](IAddressable grain)
MyGrain.SnapshotTimerFired() line 1029
TestTimerRegistry.FireAllAsync() line 37

using await Silo.FireAllTimersAsync(); on var myGrain = await Silo.CreateGrainAsync<MyGrain>(myguid, myspecialstring);

timer is registered in OnActivateAsync

timer callback looks like this:

        private async Task SnapshotTimerFired()
        {
            var me = this.AsReference<IMyGrain>();

            await me.CheckForExpiredCallsAndSendSnapshotHACK();
        }

the AsReference is used so the timer execution counts as a grain call.

does the testkit handle this, or it this some other grain having an issue?

jkonecki commented 1 year ago

Hi Alex, you can use the following workaround. Add the below extension method and use it instead of AsReference<T>(). It will catch the exception during testing and return your mocked object.

    public static class GrainExtensions
    {
        public static TGrainInterface AsSafeReference<TGrainInterface>(this IAddressable grain)
        {
                        try
            {
                return grain.AsReference<TGrainInterface>();
            }
            catch (ArgumentException ex) when (ex.Message.Contains("Passing a half baked grain as an argument")) 
            {
                return (TGrainInterface)grain;
            }
        }
    }