1

Trying to follow the example on how to test a Saga that uses DI (https://masstransit-project.com/usage/testing.html#testing-using-dependency-injection)

 var provider = new ServiceCollection()
    .AddMassTransitInMemoryTestHarness(cfg =>
    {
        cfg.AddSagaStateMachine<TStateMachine, TInstance>()
            .InMemoryRepository();
        cfg.AddSagaStateMachineTestHarness<TStateMachine, TInstance>();
    })
    .BuildServiceProvider(true);

...

However, I'm not able to get tests working with Scheduled events on the Saga. I'm following the example at https://masstransit-project.com/usage/sagas/automatonymous.html#schedule

I've got it working with RabbitMQ by adding AddRabbitMqMessageScheduler and UseDelayedExchangeMessageScheduler in my messagebus configuration.

In the tests I get the following exception:

The payload was not found: MassTransit.MessageSchedulerContext

I guess it's because I haven't configured the provider with the right things. I'm trying to find the correct extension methods but I guess there might not be support for that in combination with AddMassTransitInMemoryTestHarness?

Joel
  • 8,502
  • 11
  • 66
  • 115
  • Did your test actually work? Could you please post it and it's configuration here? I'm struggling with making mine work :( – Marek M. Aug 08 '22 at 10:59
  • @MarekM. I added an answer with parts of our tests, hopefully that will be enough to get you going. – Joel Aug 08 '22 at 11:27

2 Answers2

2

There is support, you just have to configure it:

TestHarness = provider.GetRequiredService<InMemoryTestHarness>();
TestHarness.OnConfigureInMemoryBus += configurator =>
{
    configurator.UseDelayedMessageScheduler();
};

This must execute before starting the harness.

Chris Patterson
  • 28,659
  • 3
  • 47
  • 59
1

This isn't a complete example that you can copy paste, but it contains the parts we needed to get it working.

    public class UpdateSagaTests
    {
        private const string TestDatabaseName = "saga-test-db";

        private readonly DbContextOptions<DataContext> _options = new DbContextOptionsBuilder<DataContext>()
            .UseInMemoryDatabase(TestDatabaseName)
            .Options;

        private readonly ServiceProvider _serviceProvider;
        private readonly Task<InMemoryTestHarness> _harnessSetup;
        private readonly DataContext _dataContext;

        public UpdateSearchProfileItemsSagaTests()
        {
            _dataContext = new DataContext(_options);
            _serviceProvider = SetupServiceProvider();
            _harnessSetup = SetupTestHarnessAsync();
        }

        [Fact]
        public async Task ShouldTestSagaHappyFlow()
        {
            var harness = await _harnessSetup;

            try
            {
                // Publish the event that triggers the Saga
                await harness.Bus.Send<IUpdateCommand>(new UpdateCommand
                {
                    SagaId = sagaId,
                    Id = id,
                    Max = 100,
                });

                var sagaHarness = _serviceProvider.GetRequiredService<ISagaStateMachineTestHarness<UpdateSaga, UpdateSagaState>>();

                // Make sure that the event has been consumed and that a saga has been created
                Assert.True(await sagaHarness.Consumed.Any<IUpdateCommand>());
                Assert.True(await sagaHarness.Created.Any(x => x.CorrelationId == sagaId));

                // Validate the state of the saga
                var saga = sagaHarness.Created.Contains(sagaId);
                Assert.Equal(id, saga.Id);
                Assert.Equal(100, saga.Max);
                Assert.Equal(4, saga.CurrentState); // 4 == Updating

                // The Saga should have produced the following event
                Assert.True(await harness.Published.Any<IUpdateSagaCreatedEvent>(context =>
                    context.Context.Message.SagaId == sagaId &&
                    context.Context.Message.Id == id)
                );

                // The Saga should have produced the following command in response.
                var sentUpdateDataCommand = await harness.Sent
                    .SelectAsync<IUpdateDataCommand>(
                        context => context.Context.Message.Id == id)
                    .First();
                Assert.NotNull(sentUpdateDataCommand);

                // Publish an event that will update the saga.
                await harness.Bus.Publish<IDataUpdatedEvent>(new DataUpdatedEvent
                {
                    SagaId = sagaId,
                    ...
                });

                // Make sure that the Saga has processed this event
                Assert.True(await sagaHarness.Consumed.Any<IDataUpdatedEvent>(context => context.Context.Message.UpdateInstanceId == sentUpdateDataCommand.Context.Message.UpdateInstanceId));

                // And the saga should now have been finalized
                Assert.Equal(2, saga.CurrentState); // 2 == Final
            }
            finally
            {
                // TODO: For some reason the test will hang on this, until the scheduled timeout triggers
                // await harness.Stop();

                await _serviceProvider.DisposeAsync();
            }
        }

        private ServiceProvider SetupServiceProvider()
        {
            // Setup all the dependencies that the Saga has

            
            return new ServiceCollection()
                .AddScoped<ILogger<CreateSagaHandler>>(_ => new NullLogger<CreateSagaHandler>())
                .AddScoped<CreateSagaHandler>()
                ...
                .AddDbContext<DataContext>(options => options.UseInMemoryDatabase(TestDatabaseName))
                .AddMassTransitInMemoryTestHarness(cfg =>
                {
                    cfg.AddMessageScheduler(new Uri("loopback://scheduled/"));
                    cfg
                        .AddSagaStateMachine<UpdateSaga, UpdateSagaState>()
                        .Endpoint(configurator =>
                        {
                            configurator.Name = queueName;
                        })
                        .InMemoryRepository();

                    cfg.AddMassTransitTestHarness(configurator =>
                    {
                        configurator.AddSagaStateMachine<UpdateSaga, UpdateSagaState>();
                    });

                    EndpointConvention.Map<IUpdateDataCommand>(new Uri($"loopback://{nameof(IUpdateDataCommand)}"));
                    EndpointConvention.Map<IUpdateCommand>(new Uri($"loopback://localhost/{queueName}"));
                })
                .BuildServiceProvider(true);
        }

        private async Task<InMemoryTestHarness> SetupTestHarnessAsync()
        {
            var harness = _serviceProvider.GetRequiredService<InMemoryTestHarness>();

            harness.OnConfigureInMemoryBus += configurator =>
            {
                configurator.UseDelayedMessageScheduler();
                configurator.UseInMemoryOutbox();
            };

            await harness.Start();
            return harness;
        }
    }

}
Joel
  • 8,502
  • 11
  • 66
  • 115