Reactive Domain

Integration Tests

In reactive domain, there is a lot of utilities for testing against fake event stores. Due to this, there is an ability to create a near 1-1 realistic system test to ensure the pieces of the system come together and work as expected.

You can start this by building a WebApplicationFactory<TStartup> class, and within the ConfigureWebHost method, implement the ConfigureTestServices to include the mock event store connection.

public class ApiApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class {
    public static TestTimeSource TimeSource { get; } = new TestTimeSource(); // from ReactiveDomain
    public static IClock Clock { get; } = new Clock(); // implement similar in your solution to control "time" within the system.
    
    protected override void ConfigureWebHost(IWebHostBuilder builder) {
        // mock your environment variables...
        Environment.SetEnvironmentVariable("key", "value");

        // this is the setup for Serilog.  Setup and otherwise create your logging tools
        Log.Logger = new LoggerConfiguration()
                        .MinimumLevel.Debug()
                        .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
                        .Enrich.FromLogContext()
                        //.WriteTo.Console()
                        .WriteTo.Debug()
                        .CreateLogger();
        builder.UseSerilog(Log.Logger);
        LogManager.SetLogFactory((_) => new ConsoleLogger());

        builder.ConfigureTestServices(services => {
            // Here's the magic.

            // you'll resolve the services as implemented in your actual system.
            var configuredConnectionDescriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(IConfiguredConnection));
            
            // you remove the service configuration.
            services.Remove(configuredConnectionDescriptor);

            // now you create and add the mock configuration that will be used, in this instance, to prove the
            // interaction with your event store is operational.  This is in-memory, so it allows the system
            // to move fast, and be quickly disposed of.  A set of smoke tests could be run (or this could be
            // overloaded to *NOT* put the mock repository in place (e.g. using the environment variables aboce))
            services.AddSingleton<IConfiguredConnection>(svc => {
                var conn = new ReactiveDomain.Testing.EventStore.MockStreamStoreConnection("Test Fixture");
                conn.Connect();

                var configured = new ConfiguredConnection(
                    conn,
                    new PrefixedCamelCaseStreamNameBuilder("gift"),
                    new JsonMessageSerializer()
                );

                Populate.AccountData(configured);

                return configured;
            });

            // Remove the ITimeSource that is used "live"
            var timeSourceDescriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(ITimeSource));
            services.Remove(timeSourceDescriptor);

            // Adds the "test" time source, so we can control time in respect to the LaterService
            services.AddSingleton<ITimeSource>((ctx) => TimeSource);

            // Removes our IClock that is used "live"
            var clockDescriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(IClock));
            services.Remove(clockDescriptor);

            // Adds our testing clock so we can control the clock within the system.
            services.AddSingleton<IClock>((ctx) => Clock);
        });
    }
}

With this in place, if you’re making Web API calls, there are times where you need to wait for a particular IMessage to be emitted onto the main bus of your system. This can be done using a TestQueue within the test.

In the project i’m currently working on, I needed a way to ensure that after a period of time, an event was emitted to stop a future command to succeed. At the time, I had expected that I would need a dedicated service to manage this. Chris, however, talked to me about the LaterService, as it simplified the solution by at least an order of magnitude. This is a code snippet from the project showing how to implement and use the TestQueue.

"A delay send envelope is emitted".x(async () => {
    IDispatcher dispatcher = _factory.Services.GetRequiredService<IDispatcher>();
    using (var tq = new TestQueue(dispatcher, new []{typeof(DelaySendEnvelope)})) {
        var request = new RequestOptions<Some_Request> {
            RequestTimeout = TimeSpan.FromSeconds(90),
            Message = new Some_Request {
                ... setup your request to the api endpoint.
            }
        };
        var response = await _client.RunAsync(request); // this is just a wrapper over an HttpClient.
        response.ShouldSatisfyAllConditions(
            r=>r.Status.ShouldBe(ResponseStatus.Approved)
        );
        
        tq.WaitFor<DelaySendEnvelope>(TimeSpan.FromMilliseconds(100));  // this is the magic.  we can observe the backend queue/dispatcher to ensure the envelope is emitted before continuing to advance the clock and test the timeout.
    }
});