Code Monkey home page Code Monkey logo

orleanstestkit's Introduction

Orleans TestKit

GitHub build status NuGet stable package version MIT license Discord

About

The Orleans TestKit is a community-maintained library providing mock objects that facilitate unit testing grains in applications built on the Microsoft Orleans framework. It provides a simulated grain activation context, leveraging Moq to generate test doubles for dependencies such as persistent state, reminders, timers, and streams. By simulating a grain activation context, you focus on testing the behavior of a single grain in isolation.

The official integration testing approach leverages the TestCluster. The TestCluster is a fully functional, in-memory cluster. It is faster to start than a regular cluster and provides a complete runtime. However, it may require complex configuration and custom-developed dependencies to test particular scenarios. That having been said, there are important caveats to the Orleans TestKit approach.

The simulated grain activation context does not provide the single-threaded execution model of the Microsoft Orleans runtime. It is up to you to ensure the grain activation is used appropriately. Unfortunately, this may result in abnormal method execution or behaviors that are impossible to reproduce, especially in reentrant grains.

It is also important to note that mock-based testing presents risk of coupling your test cases to the internal implementation details of the grain. This may make your code difficult to refactor and your tests brittle (see Martin Fowler's article Mocks Aren't Stubs).

It is recommended that you consider developing a mixture of tests based on both the Orleans TestKit and the TestCluster.

Getting Started

There are three branches and major versions of the Orleans TestKit. The main branch provides Orleans TestKit 8, a stable version supporting Orleans 8. The 4.x branch provides Orleans TestKit 4, a stable version supporting Microsoft Orleans 7 (during development, it was known as Orlean 4). The 3.x branch provides Orleans TestKit 3, a stable version supporting Microsoft Orleans 3.

If you are using Microsoft Orleans 8, install the latest, stable OrleansTestKit NuGet package in your test project. For example, run the following command in your Visual Studio Package Manager Console:

Install-Package OrleansTestKit

If you are using Microsoft Orleans 7, install the latest, stable version less than 8.0 of the OrleansTestKit NuGet package in your test project. For example, run the following command in your Visual Studio Package Manager Console, replacing 4.x.x with the latest version of the NuGet package less than 8.0:

Install-Package OrleansTestKit -Version 4.x.x

If you are using Microsoft Orleans 3, install the latest, stable version less than 4.0 of the OrleansTestKit NuGet package in your test project. For example, run the following command in your Visual Studio Package Manager Console, replacing 3.x.x with the latest version of the NuGet package less than 4.0:

Install-Package OrleansTestKit -Version 3.x.x

Refer to the unit tests project to learn how to create test fixtures using the Orleans TestKit.

Contributing

Either Visual Studio or Visual Studio Code may be used for development. Visual Studio provides a richer experience, especially when it comes to debugging. Visual Studio Code providers a lightweight experience and still works with the majority of the tooling.

Visual Studio

  1. In Visual Studio, open the OrleansTestKit.sln solution.

  2. Recommended: Install the CodeMaid extension.

Visual Studio Code

  1. In Visual Studio Code, open the folder containing the OrleansTestKit.sln solution file.

  2. Recommended: Open Visual Studio Code's extensions panel, and install all of the recommended extensions.

Community

  • Chat about all things Orleans on the official Discord server.
  • Report bugs and ask questions about the Orleans TestKit by opening a new GitHub Issue. Please be sure to note which version of the Orleans TestKit you are using.

License

This project is released under the MIT license.

orleanstestkit's People

Contributors

adamwyzgol avatar badabunga avatar christiansparre avatar cmeyertons avatar darkcow avatar dsarfati avatar el2iot2 avatar enewnham avatar ericgla avatar fennekin23 avatar freever avatar hazzik avatar ilchert avatar jkonecki avatar martinothamar avatar michaeltdaniels avatar oising avatar pentp avatar romanx avatar seniorquico avatar spingee avatar srollinet avatar wattsm avatar zeus82 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orleanstestkit's Issues

Timeout.InfiniteTimeSpan should execute only once with RegisterTimer

Hi,

I noticed that the Timeout.InfiniteTimeSpan when it's used with the RegisterTimer doesn't work as expected. It's should only executed once, but it appears not to be.

Here is a dummy RegisterTimer

public Task DummyTimer() { RegisterTimer( o => SimpleMethod(), "dummy", TimeSpan.FromSeconds(5), Timeout.InfiniteTimeSpan) };

Here is the dummy test :

await DummyTimer() await Silo.FireAllTimersAsync(); service.Verify(s => s.SimpleMethod(), Times.Once ); await Silo.FireAllTimersAsync(); service.Verify(s => s.SimpleMethod(), Times.Once );

Here is the problem, the method SimpleMethod() is called two time, but should be only called once.

Here is the sample of the implementation of the class GrainTimer.cs. As we can see the INFINITE_TIMESPAN should dispose the timer.

}finally { previousTickTime = DateTime.UtcNow; currentlyExecutingTickTask = null; // if this is not a repeating timer, then we can // dispose of the timer. if (timerFrequency == Constants.INFINITE_TIMESPAN) DisposeTimer(); }

Grain Call Filters

Is there any support for Grain Filters while using this test kit? I have grains that implement IIncomingGrainCallFilter, however it seems like the Invoke call is not executed when a grain method is called from my unit test.

Calling this.GetPrimaryKey() from base class is failing

I have a grain which is inheriting from a GrainBase. In the GrainBase I have a method which is using this.GetPrimaryKey() to perform another operation. I'm having the following error on this.GetPrimaryKey() when running the tests only.

Message: 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 name: grain

Failing with Orleans 2.3.0

Not sure what change to the core Orleans is. But after upgrading to Orleans 2.3.0 I am getting this exception while subscribing to streams. I suppose they added a new SubscribeAsync method

System.AggregateException : One or more errors occurred. (Method 'SubscribeAsync' in type 'Orleans.TestKit.Streams.TestStream`1' from assembly 'OrleansTestKit, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.)
       ---- System.TypeLoadException : Method 'SubscribeAsync' in type 'Orleans.TestKit.Streams.TestStream`1' from assembly 'OrleansTestKit, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
       Stack Trace:
            at System.Threading.Tasks.Task.WaitAllCore(Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken)
            at System.Threading.Tasks.Task.WaitAll(Task[] tasks, Int32 millisecondsTimeout)
            at Orleans.TestKit.TestGrainLifecycle.TriggerStart()
            at Orleans.TestKit.TestKitSilo.CreateGrain[T](IGrainIdentity identity)
            at Orleans.TestKit.TestKitSilo.CreateGrain[T](String id)
         C:\sc\product_windows\Mri.Core.Test\Displays\DisplayActorTest.cs(235,0): at Mri.Test.Displays.DisplayActorTest.SetUnitLocationAsync_ShouldSet()
         --- End of stack trace from previous location where exception was thrown ---
         ----- Inner Stack Trace -----
            at Orleans.TestKit.Streams.TestStreamProvider.GetStream[T](Guid streamId, String streamNamespace)

How to test GetPrimaryKeyLong?

I am trying to mock some code with GetPrimaryKeyLong call and can't come to success. IActivationData Data inside grain is null.

StreamProvider missing for loose stream probes

When running in loose stream creation mode, the stream provider will always be missing. Enable the stream provider to lazy create providers if Silo.Options.StrictStreamProbes is disabled

Add support for persistent state facets

Orleans 2.3.0 introduced a persistent state facet. With the new system, grain classes do not need to extend Grain<TGrainState> to use a built-in storage provider (they still need to extend Grain). This essentially trades the State property for constructor arguments passed via dependency injection. Unlike the single State property of Grain<TGrainState>, the constructor may take multiple state arguments. Additionally, each state argument may be mapped to different storage providers. Unfortunately, official docs on the new system aren't yet published.

Here's a simple example of a grain with a single state object (the "normal" pattern for a Grain<TGrainState> class):

public sealed class ColorGrainWithFacet : Grain, IColorGrain
{
    public ColorGrainWithFacet(
        [PersistentState("State")] IPersistentState<ColorGrainState> state)
    {
        this.state = state;
    }

    ...

Here's an advanced example of a grain with multiple state objects obtained from different storage providers:

public sealed class ColorGrainWithFacet : Grain, IColorGrain
{
    public ColorGrainWithFacet(
        [PersistentState("State")] IPersistentState<ColorGrainState> state, // default storage provider
        [PersistentState("Counters", "DifferentProvider")] IPersistentState<CounterGrainState> counters)
    {
        this.state = state;
        this.counters = counters;
    }

    ...

There are some significant implementation differences, and the new system doesn't currently work with the test kit's CreateGrainAsync method and its StorageManager.

First, Orleans has special logic to create and inject IPersistentState<TGrainState> arguments using a factory associated with the [PersistentState] attribute. It doesn't work to simply add an IPersistentState<TState> to our dependency injection container before calling CreateGrainAsync. Here's a standlone example (based on Moq) that identifies the required services:

// Arrange
TestKitSilo silo = ...;
TGrainState state = ...;

var mockState = new Mock<IPersistentState<TGrainState>>();
mockState.SetupGet(o => o.State).Returns(state);

var mockMapper = new Mock<IAttributeToFactoryMapper<PersistentStateAttribute>>();
mockMapper.Setup(o => o.GetFactory(It.IsAny<ParameterInfo>(), It.IsAny<PersistentStateAttribute>())).Returns(context => mockState.Object);

silo.AddService(mockMapper.Object);

// Act
var grain = await this.silo.CreateGrainAsync<TGrain>(...);
...

// Assert
...

Second, the default Orleans factory associated with the [PersistentState] attribute works with storage providers pulled directly from the dependency injection container. Unlike Grain<TGrainState>, the new system never calls IGrainRuntime.GetStorage<TGrainState>. The new system simply bypasses the test kit's StorageManager in tests.

Finally, as noted above, the new system supports multiple state objects per grain instance. This doesn't work with the the test kit's StorageManager which assumes there is at most one state object per grain instance.

The test kit should provide new utilities for unit tests to work with persistent state facets. I have some WIP tests shaping up in my fork if anyone else is currently looking at testing persistent state facets.

Await FireTimer

In Orleans, timer callback returns Task, but in test framework, we can't await them, can we add FireAllTimersAsync?

Add support for retrieving dynamically created mocks

I'm trying to test an interaction between 2 grains - similar to Ping Pong test example.

Currently the only way to obtain a Moq.Mock object for the other grain (Pong) is to add the probe before the main gain (Ping) method executes. I use SpecFlow tests and have no way of knowing before the main grain executes what kind of verification will execute later.

The dynamically created Moq.Mock instance (

) is discarded in TestGrainFactory as only the .Object is stored.

I would like to extend TestGrainFactory with GetProbe method to allow users to retrieve any probe created during the test. All Moq.Mock objects will be stored in Dictionary<string, Moq.Mock> _probeMocks collection.

Need a new version for v3.3.0 support

dotnet/orleans#6580 added a new property for IStorage<TState> which is not implemented by current version of TestKit:
System.TypeLoadException: Method 'get_RecordExists' in type 'Orleans.TestKit.Storage.TestStorage' from assembly 'OrleansTestKit, Version=3.1.2.0, Culture=neutral, PublicKeyToken=null' does not have an implementation..

This property will be included in v3.3.0 by dotnet/orleans#6686

Grain .State NullReferenceException in OnActivateAsync

Using the latest 2.0.0-beta3 and getting a NullReferenceException in OnActivateAsync when trying to read or modify state from there. Looks like the private field storage is null on the grain object.

Reading and setting state outside of OnActivateAsync works fine. Both scenarios work fine when using the grains in a normal silo instance.

TestKit support for JournaledGrains

I was looking at the different examples but I couldn't find an example of a journaled grain that is being tested. Is this supported? I just want to call a method an verify that the "Raise event" is being called.

My grain is a journaled grain with a [LogConsistencyProvider(ProviderName = "EventStorage")] and when activating this grain it would just crash with the following exception:

   at Orleans.EventSourcing.JournaledGrain`2.OnActivateAsync()
   at Orleans.Grain.<Participate>b__31_0(CancellationToken ct)
   at Orleans.LifecycleExtensions.Observer.OnStart(CancellationToken ct)
   at Orleans.TestKit.TestGrainLifecycle.<>c.<TriggerStartAsync>b__2_1(ValueTuple`2 x)
   at System.Linq.Enumerable.SelectIPartitionIterator`2.PreallocatingToArray(Int32 count)
   at System.Linq.Enumerable.SelectIPartitionIterator`2.ToArray()
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at Orleans.TestKit.TestGrainLifecycle.TriggerStartAsync()
   at Orleans.TestKit.TestKitSilo.<CreateGrainAsync>d__31`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Translink.Vbn.Aggregates.Tests.CompanyAggregateRootContext.<Execute>d__6.MoveNext() 

So something doesnt work in the OnActivate. This exception occurs when calling the CreateGrainAsync method

        Grain = await Silo.CreateGrainAsync<CompanyAggregateRoot>(CompanyId);

When I directly use the grain factory i do not get any DI which this grain needs (and the version property is not set). Are there better ways to test journaled grains? Or do I just not test them (which feels kinda strange).

Change (remove?) type constraints on AddStreamProbe

Currently, AddStreamProbe requires reference type for the "message type" that is sent over the stream. But it should work with value types as well.

Luckily, the direct call works (so workarounds are possible):

var stream = Silo.StreamProviderManager.AddStreamProbe<Guid>(Guid.Empty, "namespace", "Default");

Question for this.AsReference<IMyGrain> in timers

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?

TestStream OnNextBatchAsync

Are there any plans to support OnNextBatchAsync in TestStream?

public Task OnNextBatchAsync(IEnumerable<T> batch, StreamSequenceToken token = null) =>

Currently, we have been forced to switch to using OnNextAsync to avoid failing unit tests. This can have undesired performance / cost implications depending on the actual stream implementation used for the release.

It.IsAny<Id>

Hi guys

Is there any way to configure TestKitSilo to return a mock grain when it is called with any id?

I have a case where a grain will generate a new Guid and then activate a grain of another type using that new Guid in order to create a new instance of the target grain.

Thinking in terms of MoqI would use something like It.IsAny() but TestKitSilo requires an actual Id. Since I am Guid.NewGuidding it inside my grain I can't inject a predetermined id without doing a lot of unnecessary factory stuff.

Is there any way to achieve this currently?

Thanks!

Unable to test Reminders - Orleans 7.x

I'm running into the following issue when trying to test reminders after upgrading to Orleans 7.x. I suspect a change would be needed in Orleans itself.

GrainReminderExtensions class has the following check for RuntimeContext.Current which fails inside unit test:

    private static IReminderRegistry GetReminderRegistry(IGrainContext grainContext)
    {
        if (RuntimeContext.Current is null) ThrowInvalidContext();
        return grainContext.ActivationServices.GetRequiredService<IReminderRegistry>();
    }

    private static void ThrowInvalidContext()
    {
        throw new InvalidOperationException("Attempted to access grain from a non-grain context, such as a background thread, which is invalid."
            + " Ensure that you are only accessing grain functionality from within the context of a grain.");
    }

I came up with the below workaround - reflection to the rescue ;-)

var grain = await this.Silo.CreateGrainAsync<TGrain>("grain id");

var grainContext = grain.GetType().GetProperty("GrainContext", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(grain);
var runtimeContextType = Assembly.GetAssembly(typeof(IAddressable)).GetType("Orleans.Runtime.RuntimeContext");
runtimeContextType.GetMethod("SetExecutionContext", BindingFlags.Static | BindingFlags.NonPublic, new[] { typeof(IGrainContext) }).Invoke(null, new object[] { grainContext });

Stream VerifySend<T> support for extended classes

very typically streams are setup for a base class, then derived classes are passed down the stream to create different events within the stream.

class extendedDomainEvent : domainEventBase
{
  public int MoreStuff {get;set;}
var stream = Silo.AddStreamProbe<domainEventBase>(staffId, "sillynamespace");
var msg = new extendedDomainEvent() { MoreStuff = 100 };
await stream.OnNextAsync(msg);

because VerifySend is expecting only T, its not possible to see the derived type and verify its properties.

stream.VerifySend(m=> m.MoreStuff == 100) doesnt work

public void VerifySend(Func<T, bool> check) =>

VerifySend needs to be able to recognize an extended class, one to make sure its the right type and second to check its properties

perhaps something like:

        public void VerifySend<S>(Func<S, bool> check) where S : T =>
            VerifySend(check, Times.Once());

        public void VerifySend<S>(Func<S, bool> check, Times times) where S : T =>
            _mockStream.Verify(s => s.OnNextAsync(It.Is<S>(a => check(a)), It.IsAny<StreamSequenceToken>()), times);

Support for Orleans 7

Hi Guys,

Love your project which makes the unit testing with Orleans very easy.
I know that Orleans 4.0 is not released yet but we are trying to migrate our current Orleans version to the latest preview ( currently preview 2). We used this Project to build our Unit Tests for our Application.

I am currently in the upgrading process and noticed a bunch of errors regarding reference type problems but I assume there are many more
My Question is : Are there any plans to support the upcoming Version ?

Fail after upgrade projec to Orleans 2.0

After I upgrade our project to 2.0 I get error Message: System.TypeLoadException : Method 'get_ServiceId' in type 'Orleans.TestKit.TestGrainRuntime' from assembly 'OrleansTestKit, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. on all test

Typed grain probes.

Add grain key types to the probes to ensure the right Id type is being passed in based on the interface.

IAddressable.GetPrimary(Type)Key throws ArgumentException

This pattern works fine at runtime in Orleans, but fails in the test kit.

namespace Tests
{
    public class BaseGrain : Grain
    {
        // IAddressable.GetPrimaryKeyString()
        protected string MyGrainKey => this.GetPrimaryKeyString();
    }

    public class DumbGrain : BaseGrain, IDumb
    {
        public Task<string> Duh()
        {
            // IGrainWithStringKey.GetPrimaryKeyString()
            return Task.FromResult(this.GetPrimaryKeyString());
        }

        public Task<string> Doh()
        {
            return Task.FromResult(MyGrainKey);
        }
    }

    public interface IDumb : IGrainWithStringKey
    {
        Task<string> Duh();

        Task<string> Doh();
    }

    public class BasicGrainTests : TestKitBase
    {
        [Fact]
        public async Task TestDumbGrain()
        {
            IDumb grain = await Silo.CreateGrainAsync<DumbGrain>("foo");

            // OK
            await grain.Duh();

            /*
             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')
             at Orleans.GrainExtensions.GetGrainId(IAddressable grain)
             at Orleans.GrainExtensions.GetPrimaryKeyString(IAddressable grain)
             */
            await grain.Doh();
        }
    }

GrainProbeTests.ProbeWithClassPrefix is not testing anything. Fixing it is raising an exception.

While looking at the test project provided as documentation I saw some issues in the test GrainProbeTests.ProbeWithClassPrefix. Here is the code of the test for reference:

public async Task ProbeWithClassPrefix()
{
    Silo.AddProbe<IDevice>("Android", "TestGrains.DeviceAndroidGrain");
    Silo.AddProbe<IDevice>("IOS", "TestGrains.DeviceIosGrain");

    var managerGrain = await this.Silo.CreateGrainAsync<DeviceManagerGrain>(0);
    var iosGrain = await managerGrain.GetDeviceGrain("IOS");
    var androidGrain = await managerGrain.GetDeviceGrain("Android");

    (await iosGrain.GetDeviceType()).Should().Equals("IOS");
    (await iosGrain.GetDeviceType()).Should().Equals("Android");
}

The first issue is the duplicated usage of the iosGrain. The second issue is the usage of the method Equals which is effectively doing nothing since its returned value is not used.

The last two lines of the test should be changed to:

(await iosGrain.GetDeviceType()).Should().Be("IOS");
(await androidGrain.GetDeviceType()).Should().Be("Android");

Unfortunately making this change make the test fail. It is failing on the line (await iosGrain.GetDeviceType()).Should().Be("IOS"); which was unexpected.

The exception raised is:

Expected result to be "IOS", but found <null>.
   at FluentAssertions.Execution.XUnit2TestFramework.Throw(String message) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\XUnit2TestFramework.cs:line 32
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1 failReasonFunc) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Execution\AssertionScope.cs:line 181
   at FluentAssertions.Primitives.StringValidator.ValidateAgainstNulls() in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Primitives\StringValidator.cs:line 55
   at FluentAssertions.Primitives.StringValidator.Validate() in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Primitives\StringValidator.cs:line 30
   at FluentAssertions.Primitives.StringAssertions.Be(String expected, String because, Object[] becauseArgs) in C:\projects\fluentassertions-vf06b\Src\FluentAssertions\Primitives\StringAssertions.cs:line 42
   at Orleans.TestKit.Tests.GrainProbeTests.<ProbeWithClassPrefix>d__11.MoveNext() in D:\OrleansTestKit\test\OrleansTestKit.Tests\Tests\GrainProbeTests.cs:line 157
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

I wasn't able to fix that exception as my knowledge of the Orleans test kit is limited.

Removed `sealed` keyword

Several key classes (StorageManager, TestStorage, TestKitSilo) are marked as sealed.

This means their behaviour cannot be customized.

I'm currently using a storage provider and grains that are event-sourced, and need custom behaviour around state handling (state is not just a single object, it's a stream of events). I also want to allow for richer tests when reading / writing state.

The alternative is to fork the whole project, wchich I would like to avoid.

Would you please consider dropping sealed keywords? I'm happy to make a PR.

It's a bit urgent for me, so would hugely appreciate your response.

How to unit test GrainService

Hello,

First, thank you for your amazing work !

I would like to know if it's possible to unit test GrainService using something like TestKitBase.
Is it possible to do it without creating a TestCluster ?

Thank you in advance,

Unable to create multiple grains even though the test project does

System.Exception : A grain has already been created in this silo. Only 1 grain per test silo should ever be created. Add grain probes for supporting grains.

Here is my code:

using System;
using System.Reflection;
using System.Threading.Tasks;
using FakeItEasy;
using AutoFixture;
using FluentAssertions;
using GenericPlatform.Interfaces;
using GenericPlatform.Silo.Grains;
using Microsoft.Extensions.Logging;
using NUnit.Framework;
using Orleans;
using Orleans.Runtime;
using Orleans.TestKit;
using Orleans.TestKit.Services;

namespace GenericPlatform.Grains.Tests
{
    public class EntityDefinitionGrainTests:TestKitBase
    {
        private EntityDefinition state;

        [OneTimeSetUp]
        public void OneTimeSetup()
        {
            this.state = new EntityDefinition();
            SetupState<EntityDefinition, EntityDefinitionGrain>(this.state);

        }
        private void SetupState<TState,TGrain>(TState state)
        {
            var mockState = A.Fake<IPersistentState<TState>>();
            A.CallTo(() => mockState.State).Returns(state);

            var mockMapper = A.Fake<IAttributeToFactoryMapper<PersistentStateAttribute>>();
            A.CallTo(() => mockMapper.GetFactory(A<ParameterInfo>._, A<PersistentStateAttribute>._))
                .Returns(context => mockState);

            Silo.AddService(mockMapper);
            var logger = A.Fake<ILogger<TGrain>>();
            Silo.ServiceProvider.AddService(logger);
        }

        [Test]
        public async Task VersionShouldBe_Zero_On_Initialization()
        {
            this.state = new EntityDefinition();
            var grain = await Silo.CreateGrainAsync<EntityDefinitionGrain>("0");
            
            var result = await grain.GetDefinition();
            result.Version.Should().Be(0);
            result.Properties.Should().BeEmpty();
        }
        
        [Test]
        public async Task VersionShouldBe_1_On_AfterOneOperation()
        {
            this.state = new EntityDefinition();
            
             var grain = await Silo.CreateGrainAsync<EntityDefinitionGrain>("0");
            await grain.AddProperty("Name", typeof(string).FullName!);
            var result = await grain.GetDefinition();
            result.Version.Should().Be(1);
            result.Properties.Should().ContainKey("Name");
        }
    }
}

OnNextBatchAsync on TestStream does not have an implementation

Upgraded to 2.3.1 today. All unit tests that use TestStream now fail, with the following error:

StackTrace: at Orleans.TestKit.Streams.TestStreamProvider.GetStream[T](Guid streamId, String streamNamespace) --- End of stack trace from previous location where exception was thrown --- Result Message: System.TypeLoadException : Method 'OnNextBatchAsync' in type 'Orleans.TestKit.Streams.TestStream'1' from assembly 'OrleansTestKit, Version=2.3.1.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

Using OrleansTestKit 2.3.1, Orleans.Core.Abstractions 2.3.1

Similar to #49 , but the odd thing is that TestStream.OnNextBatchAsync already exists with the same signature in OrleansTestKit:2.3.1

Add support for `IAsyncStream.GetAllSubscriptionHandles`

The test kit's IAsyncStream<T> implementation (TestStream<T>) lacks a GetAllSubscriptionHandles implementation. It simply throws a NotImplementedException.

The TestStream<T>.SubscribeAsync overloaded methods paired with the TestStreamSubscriptionHandle<T>.UnsubscribeAsync method should be used to maintain a list of active subscription handles. The TestStream<T>.GetAllSubscriptionHandles method can return a copy of this list.

Add support for transactional state facet

Since the release of Orleans 2.1.0 grains now support transactions.

For a grain to use transactions it should be instanciated with a ITransactionalState<TState> facet:

public AccountGrain(
    [TransactionalState("balance", "TransactionStore")]
    ITransactionalState<Balance> balance)
{
    this.balance = balance ?? throw new ArgumentNullException(nameof(balance));
}

It would be great to be able to unit test grain with a transactionnal state facet with the test kit. Right now trying to get a grain with a ITransactionalState<TState> facet like so:

var grain = await Silo.CreateGrainAsync<AccountGrain>(Guid.NewGuid());

Is throwing this exception:

Orleans.Runtime.OrleansException : Attribute mapper Castle.Proxies.IAttributeToFactoryMapper`1Proxy failed to create a factory for grain type AccountGrain
   at Orleans.Runtime.ConstructorArgumentFactory.ArgumentFactory.GetFactory[TMetadata](IServiceProvider services, ParameterInfo parameter, IFacetMetadata metadata, Type type) in D:\build\agent\_work\23\s\src\Orleans.Runtime\Facet\ConstructorArgumentFactory.cs:line 90

Here is a naive implementation that fulfil the ITransactionalState interface and enable testing such grains:

class TestTransactionalState<TState> : ITransactionalState<TState>
    where TState : class, new()
{
    private readonly TState _state = new TState();

    public Task<TResult> PerformRead<TResult>(Func<TState, TResult> readFunction)
    {
        return Task.FromResult(readFunction(_state));
    }

    public Task<TResult> PerformUpdate<TResult>(Func<TState, TResult> updateFunction)
    {
        return Task.FromResult(updateFunction(_state));
    }
}

Standalone usage example:

var state = new TestTransactionalState<Balance>();
var grain = new AccountGrain(state);

This implementation works but something integrated to the test kit would be better.

Argument issue on CreateGrainAsync

Hi!

The function CreateGrainAsync systematically returns an ArgumentException to me when I try to create a grain. My grain extends Orleans.IGrainWithIntegerKey.

When I do:
var grain = await Silo.CreateGrainAsync<IHelloGrain>(0L);
I have this error: error CS1503: Argument 1: cannot convert from' long' to' System.Guid'

If I try to use a System.Guid, I have an Argument 1: cannot convert from 'System.Guid' to 'long', which seems logical.

I use Orleans 2.3.5 and TestKit 2.3.5.

My complete code:

// IHelloGrain.cs
public interface IHelloGrain : Orleans.IGrainWithIntegerKey
{
    Task<string> SayHello(string greeting);
}

// HelloGrain.cs
public class HelloGrain : Orleans.Grain, IHelloGrain
{
    public Task<string> SayHello(string greeting)
    {
        return Task.FromResult($"You said: '{greeting}', I say: Hello!");
    }
}

// HelloGrainTest.cs
[Fact]
public async Task CreateGrain()
{
    var grain = await Silo.CreateGrainAsync<IHelloGrain>(0L);
    grain.Service.Should().NotBeNull();
}

Is it a known issue or something wrong with my code?

Unit testing streams and unsubscribing

hey folks,
I'm having a problem testing streams with OrleansTestKit and perhaps someone here could throw some light on why that is happening
My sample code is up here:
https://gist.github.com/pawanrao/6c0c682e490b625a2db7d08098712b3c

The problem I'm facing is that when I try to assert that the stream subscriber for OnNextAsync is called correctly, the unsubscribe seems to happen before the test has executed and I run into the following error:

System.InvalidOperationException : Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Linq.Enumerable.SelectListIterator`2.MoveNext()
   at System.Threading.Tasks.Task.WhenAll(IEnumerable`1 tasks)

   at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult() in D:\a\1\s\src\NUnitFramework\framework\Internal\TaskAwaitAdapter.cs:line 99
   at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(Func`1 invoke) in D:\a\1\s\src\NUnitFramework\framework\Internal\AsyncToSyncAdapter.cs:line 60
   at NUnit.Framework.Internal.Commands.TestMethodCommand.Execute(TestExecutionContext context) in D:\a\1\s\src\NUnitFramework\framework\Internal\Commands\TestMethodCommand.cs:line 64
   at NUnit.Framework.Internal.Execution.SimpleWorkItem.PerformWork() in D:\a\1\s\src\NUnitFramework\framework\Internal\Execution\SimpleWorkItem.cs:line 58

I'm a little confused if this is a limitation of OrleansTestKit or if I'm unsubscribing to the stream at the wrong place
Any help would be appreciated!

I posted on the Orleans repo dotnet/orleans#6371 and they suggested posting here since unit testing is involved.

Make the TestReminderRegistry behave more the the actual runtime ReminderRegistry

Hi, we ran into a small issue using the test kit to test grain behavior when trying to unregister an unknown reminder.

On IReminderRegistry.GetReminder the TestReminderRegistry will throw a KeyNotFoundException instead of returning null as the runtime reminder registry does.

I have made the changes to support our needs and that behavior with this: christiansparre@69e3d9d

How does it look? Feedback for more cleanup and tests to fit the OrleansTestKit are welcome :)

PS: Thank you for an excellent unit testing library for Orleans!

Breaking changes in orleans 3.1

With a new version of Orleans (3.1.1), I got an error

'Unable to load one or more of the requested types.
Method 'GetStorage' on type 'Orleans.TestKit.TestGrainRuntime' from assembly 'OrleansTestKit, Version=3.0.1.0, Culture=neutral, PublicKeyToken=null' tried to implicitly implement an interface method with weaker type parameter constraints.'

It looks like this change did breaking change in IOrleansRuntime.

Orleans 2 and NETStandard

Hi!

I see there's some work being done to support Orleans 2.0, which is great.
I see the 0.1.4-beta is targeting netcoreapp2.0, but we are using .NET 4.6.1 and so isn't compatible. Any chance this could switch to netstandard2.0?

Allow streams providers to be lazily created

Since the changes were made to use 2.0.0-beta2, you now need to create a stream probe in order for the stream provider to exist. This should be a quick and easy change to the TestStreamProviderManager.

NuGet 2.1.0 Package hasn't been generated/published?

As mentioned here, there hasn't yet been a 2.1.0 release to NuGet. Opening an issue in the hopes that I may just be hitting @dsarfati 's notification filters.

Current status in NuGet is:
image

And I am hoping to decommission my fork of OrleansTestKit in a project, as I prefer to consume the nuget package. Thanks for your consideration.

`ClearStateAsync` does not create a new state object instance

I've been looking into areas where the TestKit behaviors differ from the Orleans runtime. I discovered that we aren't resetting the state property (in grains that extend Grain<TGrainState>) after calling ClearStateAsync. Right now, we're trying to mock both IStorageProvider and IStorage. We should look into swapping out the fake IStorage for StateStorageBridge. It's public and used by both Grain<TGrainState> and the new facet system.

Here's an example test that fails (by asserting on the state):

// Arrange
var storage = this.silo.StorageManager.GetStorage<ColorGrainState>();
storage.State = new ColorGrainState
{
    Color = Color.Red,
    Id = grainId,
};

var grain = await this.silo.CreateGrainAsync<ColorGrainWithState>(this.grainId);

// Act
await grain.ResetColor();

// Assert
storage.State.Color.Should().Be(Color.Unknown);
storage.State.Id.Should().Be(Guid.Empty);

However, here's an example test that succeeds (by asserting on the storage stats):

// Arrange
var storage = this.silo.StorageManager.GetStorage<ColorGrainState>();
storage.State = new ColorGrainState
{
    Color = Color.Red,
    Id = grainId,
};

var grain = await this.silo.CreateGrainAsync<ColorGrainWithState>(this.grainId);

// Act
await grain.ResetColor();

// Assert
this.silo.StorageStats().Clears.Should().Be(1);

Remove necessity of Moq on external methods

Hi guys,

I really like the idea of this package, but having a hard dependency on Moq is not something that's palatable for my team.

I'm happy with the internal workings of this package using Moq, but would like this package to enable the end-user to use whatever mocking framework they want when using this package.

It seems like this is partially supported already with the Silo ServiceProvider (allowing an instance or a Mock -

public Mock<T> AddServiceProbe<T>() where T : class
{
var mock = new Mock<T>();
_services.Add(typeof(T), mock.Object);
return mock;
}
public T AddService<T>(T instance)
{
_services.Add(typeof(T), instance);
return instance;
}

But for grain probes, it seems like I can't register an instance of the grain via the AddProbe method on the Silo without using Moq in some way -

public static void AddProbe<T>(this TestKitSilo silo, Func<IGrainIdentity, IMock<T>> factory)
where T : class, IGrain
{
if (silo == null)
{
throw new ArgumentNullException(nameof(silo));
}
silo.GrainFactory.AddProbe(factory);
}

I'm thinking of overloading the GrainProbeExtensions' AddProbe method to allow the user to pass in simply a factory method that returns the interface of the grain rather than a IMock, as it seems like we're only using the instance in the GrainFactory regardless.

private T GetProbe<T>(IGrainIdentity identity, string grainClassNamePrefix)
where T : IGrain
{
var key = GetKey(identity, typeof(T), grainClassNamePrefix);
if (_probes.TryGetValue(key, out var grain))
{
return (T)grain;
}
//If using strict grain probes, throw the exception
if (_options.StrictGrainProbes)
{
throw new Exception($"Probe {identity.IdentityString} does not exist for type {typeof(T).Name}. " +
"Ensure that it is added before the grain is tested.");
}
else
{
IMock<IGrain> mock;
if (_probeFactories.TryGetValue(typeof(T), out var factory))
{
mock = factory(identity);
}
else
{
mock = Activator.CreateInstance(typeof(Mock<>).MakeGenericType(typeof(T))) as IMock<IGrain>;
}
//Save the newly created grain for the next call
grain = mock?.Object;
_probes.Add(key, grain);
}
return (T)grain;
}

Please let me know what you think and if I'm missing something that would make this undoable.

Thanks,

Huw

Failing with Orleans 2.0 rc1

So I updated a .net core project using orleans 2.0 beta3 to rc1. After that my tests are failing as

Test Name:	should_add_item
Test FullName:	OrleansSilo.Tests.TodoGrainImplTests.should_add_item
Test Source:	D:\code\OrleansK8S\src\OrleansSilo.Tests\TodoGrainImplTests.cs : line 24
Test Outcome:	Failed
Test Duration:	0:00:03.267

Result StackTrace:	
at Orleans.TestKit.TestKitSilo..ctor()
   at OrleansSilo.Tests.TodoGrainImplTests.BeforeEach() in D:\code\OrleansK8S\src\OrleansSilo.Tests\TodoGrainImplTests.cs:line 18
Result Message:	System.TypeLoadException : Method 'GetServices' in type 'Orleans.TestKit.Streams.TestStreamProviderManager' from assembly 'OrleansTestKit, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

My BeforeEach looks like this:

    [TestFixture]
    public class TodoGrainImplTests 
    {
        private ToDoGrainImpl grain;
        private TestKitSilo silo;
        [SetUp]
        public void BeforeEach() {
            silo = new TestKitSilo();
            grain = silo.CreateGrain<ToDoGrainImpl>("RANDOM");
        }
        
        [Test]
        public async Task should_add_item()
        {
            var todo = GetTodo();
            await grain.AddTodo(todo);

            var items = await grain.GetAll();
            Assert.IsTrue(items.Any(it => it.Id == todo.Id));
        }

Add ability to reset storage counts

Need to allow storage counts for (writes,reads,clears) to be reset. Sometimes this is needed when setup of the test grain calls WriteStateAsync. I'm thinking simply added Silo.Storage(grain).ResetCounts(); that sets all the counts to zero

Implement RecordExists

The TestStorage should implement the new RecordExists property introduced in Orleans 3.3.0. A call to WriteStateAsync should set RecordExists to true, and a call to ClearStateAsync should set RecordExists to `false.

See the discussion notes on #96.

Support beta 3.0

Hey folks!

I'm trying to use TestKit with the new beta 3.0 but it is failing:

System.TypeLoadException : Method 'GetGrain' in type 'Orleans.TestKit.TestGrainFactory' from assembly 'OrleansTestKit, Version=2.3.6.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

Is there any plans to make a least a beta package that support it soon? Otherwise will need to drop its usage.

Thanks.

[Idea] Mock Grain Factory

Add a method to setup mocked grains regardless of grain id. Currently Silo.AddProbe is the only way of adding a mocked out grain. It is impossible to mock a grain with an unknown id.

TestStreamSubscriptionHandle.ResumeAsync NotImplementedException

Hi,

We use TestKitBase for unit testing grains, but I am encountering when attempting to call a method that resumes handlers in the grain, a NotImplementedException on TestStreamSubscriptionHandle.ResumeAsync. It makes sense when I read the code, since it is actually not implemented and set to throw this exception. But is there a reason for this? Are we not meant to call grain code that resumes stream subscriptions from a unit test, or what am I missing?

Best regards

Support for custom Service Provider

Hi!

First of all, thank you for the great framework!

I'm trying to find out if there is any possibility to use a custom ServiceProvider. As far as I see, there is none. However, since my projects are based on Autofac modules, I'd like to have such functionality to avoid registering components twice.
The idea is to have a custom ServiceProvider which will first look for mock in Dictionary as it's implemented now and if no suitable implementation found, will look in Autofac container for a ordinary implementation. In this way, I'd need to manually register only services which I need to mock in a particular test.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.