Show / Hide Table of Contents

    Testing

    Silverback ships a mocked version of the message broker implementations on a different nuget package:

    • Silverback.Integration.Kafka.Testing
    • (coming soon) Silverback.Integration.RabbitMQ.Testing

    These packages allow to perform end-to-end tests without having to integrate with a real message broker.

    Unit Tests

    Here an example of an xUnit test built using Silverback.Integration.Kafka.Testing.

    public class KafkaTests
    {
        private readonly IServiceProvider _serviceProvider;
    
        // Configure DI during setup
        public InMemoryBrokerTests()
        {
            var services = new ServiceCollection();
    
            // Loggers are a prerequisite
            services.AddSingleton<ILoggerFactory, NullLoggerFactory>();
            services.AddSingleton(typeof(ILogger<>), typeof(NullLogger<>));
    
            services
                // Register Silverback as usual
                .AddSilverback()
                // Register the mocked KafkaBroker
                .WithConnectionToMessageBroker(config => config.AddMockedKafka())
                // Configure inbound and outbound endpoints
                .AddKafkaEndpoints(endpoints => endpoints
                    .Configure(config => 
                        {
                            config.BootstrapServers = "PLAINTEXT://tests"; 
                        })
                    .AddOutbound<InventoryEvent>(endpoint => endpoint
                        .ProduceTo("test-topic"))
                    .AddInbound(endpoint => endpoint
                        .ConsumeFrom("test-topic")
                        .Configure(config => 
                            {
                                config.GroupId = "my-test-consumer";
                            })))
                // Register the subscriber under test
                .AddScopedSubscriber<MySubscriber>();
    
            // ...register all other types you need...
    
            _serviceProvider = services.BuildServiceProvider();
        }
    
        [Fact]
        public async Task SampleTest()
        {
            // Arrange
    
            // Connect the broker
            await _serviceProvider.GetRequiredService<IBroker>().ConnectAsync();
    
            // Create a producer to push to test-topic
            var producer = _serviceProvider
                .GetRequiredService<IBroker>()
                .GetProducer(new KafkaProducerEndpoint("test-topic"));
    
            // Act
            await producer.ProduceAsync(new TestMessage { Content = "hello!" });
            await producer.ProduceAsync(new TestMessage { Content = "hello 2!" });
    
            // Assert
            // ...your assertions...
        }
    }
    

    Integration Tests

    Mocking the message broker is especially interesting for the integration tests, where you probably leverage the ASP.NET Core integration tests to perform a full test based on the real configuration applied in the application's startup class.

    The following code shows the simplest integration test possible, in which an object is published to the broker and e.g. a subscriber is called.

    public class IntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;
    
        public IntegrationTests(WebApplicationFactory<Startup> factory)
        {
            _factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    // Replace the usual broker (KafkaBroker)
                    // with the mocked version
                    services.UseMockedKafka();
                });
            });
        }
    
        [Fact]
        public async Task SampleTest()
        {
            // Arrange
    
            // Resolve a producer to push to test-topic
            var producer = _factory.Server.Host.Services
                .GetRequiredService<IBroker>()
                .GetProducer(new KafkaProducerEndpoint("tst-topic"));
    
            // Act
            await producer.ProduceAsync(new TestMessage { Content = "abc" });
    
            // Assert
            // ...your assertions...
        }
    }
    

    Testing helper

    The testing helpers (such has IKafkaTestingHelper) contain some methods that simplify testing with the message broker, given it's asynchronous nature.

    public class IntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;
    
        public IntegrationTests(WebApplicationFactory<Startup> factory)
        {
            _factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    services.UseMockedKafka();
                });
            });
        }
    
        [Fact]
        public async Task SampleTest()
        {
            // Arrange
    
            // Resolve the IKafkaTestingHelper (used below)
            var testingHelper = _factory.Server.Host.Services
                .GetRequiredService<IKafkaTestingHelper>();
    
            // Resolve a producer to push to test-topic
            var producer = testingHelper.Broker
                .GetProducer(new KafkaProducerEndpoint("tst-topic"));
    
            // Act
            await producer.ProduceAsync(new TestMessage { Content = "abc" });
    
            // Wait until all messages have been consumed and 
            // committed before asserting
            await testingHelper.WaitUntilAllMessagesAreConsumedAsync();
    
            // Assert
            // ...your assertions...
        }
    }
    

    IntegrationSpy

    The IIntegrationSpy ships with the Silverback.Integration.Testing package (referenced by the other Integration.Testing.* packages) and can be used to inspect all outbound and inbound messages.

    public class IntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;
    
        public IntegrationTests(WebApplicationFactory<Startup> factory)
        {
            _factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    services
                        .ConfigureSilverback()
                        .UseMockedKafka()
                        .AddIntegrationSpy();
                });
            });
        }
    
        [Fact]
        public async Task SampleTest()
        {
            // Arrange
            var testingHelper = _factory.Server.Host.Services
                .GetRequiredService<IKafkaTestingHelper>();
    
            var producer = testingHelper.Broker
                .GetProducer(new KafkaProducerEndpoint("tst-topic"));
    
            // Act
            await producer.ProduceAsync(new TestMessage { Content = "abc" });
    
            // Wait until all messages have been consumed and 
            // committed before asserting
            await testingHelper.WaitUntilAllMessagesAreConsumedAsync();
    
            // Assert
            testingHelper.Spy.OutboundEnvelopes.Should().HaveCount(1);
            testingHelper.Spy.InboundEnvelopes.Should().HaveCount(1);
            testingHelper.Spy.InboundEnvelopes[0].Message.As<TestMessage>
                .Content.Should().Be("abc");
        }
    }
    

    Mocked Kafka

    Many aspects of the Kafka broker have been mocked to replicated as much as possible the behavior you have when connected with the real broker. This new implementation supports commits, kafka events, offset reset, partitioning, rebalance, etc.

    The mocked topics can be retrieved and inspected via the GetTopic method of the IKafkaTestingHelper.

    Partitioning

    By default 5 partitions will be created per each topic being mocked. This number can be configured as shown in the following snippet. One can also specify the number of partitions per topic.

    public class IntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;
    
        public IntegrationTests(WebApplicationFactory<Startup> factory)
        {
            _factory = factory.WithWebHostBuilder(builder =>
            {
                builder.ConfigureTestServices(services =>
                {
                    services
                        .UseMockedKafka(options => options
                            .WithDefaultPartitionsCount(10)
                            .WithPartitionsCount("my-topic", 3)
                            .WithPartitionsCount("my-other-topic", 1);
                });
            });
        }
    }
    
    • Improve this doc
    GitHub E-Mail
    ↑ Back to top © 2020 Sergio Aquilini