Samples

Fixture
using FluentTesting.Common;
using FluentTesting.Common.Extensions;
using FluentTesting.Common.Interfaces;
using FluentTesting.RabbitMq;
using FluentTesting.RabbitMq.Options;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Samples.Worker.RabbitMq.ConsumptionHandlingServices;

namespace Samples.Worker.RabbitMq.Tests.Shared;

/// <summary>
/// Example of test fixture with custom options
/// </summary>
public class TestFixture : ITestFixture
{
    public IApplicationFactory ApplicationFactory { get; }

    public readonly Mock<IConsumptionHandler> ConsumptionHandlerMock = new();

    public TestFixture()
    {
        ApplicationFactory = new ApplicationFactoryBuilder<Program>()
            .RegisterServices((services, configuration) =>
                {
                    services.AddSingleton(ConsumptionHandlerMock.Object);
                })
            .UseRabbitMq((configuration, rabbitSettings) =>
            {
                configuration.AddObject("RabbitConnectionOptions", new RabbitConnectionOptions()
                {
                    HostName = rabbitSettings.Host,
                    Password = rabbitSettings.Password,
                    UserName = rabbitSettings.UserName,
                });
            }, opts =>
            {
                opts.PublisherBindings = [new Exchange() {
                    ExchangeName = "test",
                    RoutingKeys = ["testRoutingKey"]
                }];

                opts.ConsumerBindings = [

                    new Exchange()
                    {
                        ExchangeName = "consumptionTest",
                        RoutingKeys = ["RabbitMessage"],
                        QueueName = "ConsumptionTestRabbitMessageQueue"
                    }
                ];

                opts.DefaultQueueName = "testQueue";
            })
            .Build();
    }
}
Publishing on app run in worker service
using Microsoft.Extensions.Options;
using RabbitMQ.Client;
using System.Text;

namespace Samples.Worker.RabbitMq
{
    public class RabbitPublishingWorker(IOptions<RabbitConnectionOptions> opts) : BackgroundService
    {
        private readonly RabbitConnectionOptions rabbitOpts = opts.Value;

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            var factory = new ConnectionFactory
            {
                HostName = rabbitOpts.HostName,
                UserName = rabbitOpts.UserName,
                Password = rabbitOpts.Password,
            };

            using var connection = await factory.CreateConnectionAsync(stoppingToken);
            using var channel = await connection.CreateChannelAsync(null, stoppingToken);

            byte[] messageBodyBytes = Encoding.UTF8.GetBytes("Hello, world!");

            await channel.BasicPublishAsync("test", "testRoutingKey", true, new BasicProperties(), messageBodyBytes, stoppingToken);

            await channel.CloseAsync(cancellationToken: stoppingToken);
            await connection.CloseAsync(cancellationToken: stoppingToken);
        }
    }
}

Test - consumming message from rabbit to assert that it was published sucessfully
using FluentAssertions;
using FluentTesting.Common.Extensions;
using FluentTesting.RabbitMq.Extensions;
using Samples.Worker.RabbitMq.Tests.Shared;

namespace Samples.Worker.RabbitMq.Tests
{
    [Collection("RabbitMqFixture")]
    public class RabbitPublisherTests(TestFixture fixture)
    {
        [Fact]
        public async Task PublishFromApp_ShouldWork()
        {
            var cts = fixture.GetCtsWithTimeoutInSeconds();

            await Task.Delay(1000);

            var consumed = await fixture.ApplicationFactory.ConsumeRabbitMqMessage(cts.Token);

            consumed.Should().NotBeNull();

            consumed?.Payload.Should().Be("Hello, world!");
        }
    }
}
Test - publishing message to RabbitMQ which is sucessfully managed by application
using FluentAssertions;
using FluentTesting.Common.Extensions;
using FluentTesting.RabbitMq.Extensions;
using Moq;
using Samples.Worker.RabbitMq.ConsumptionHandlingServices;
using Samples.Worker.RabbitMq.Contracts;
using Samples.Worker.RabbitMq.Tests.Shared;

namespace Samples.Worker.RabbitMq.Tests
{
    [Collection("RabbitMqFixture")]
    public class RabbitConsumerTests(TestFixture fixture)
    {
        [Fact]
        public async Task ObtainingMessages_Should_Work()
        {
            var contract = new RabbitMessage()
            {
                BirthDate = DateTime.Now,
                Email = "coconut@coco.com",
                SurName = "Testschenko",
                Name = "Testicss"
            };

            var cts = fixture.GetCtsWithTimeoutInSeconds(60);

            // pseudo unmocking process to create callback to stop waiting for consumption, cause message has been consumed
            fixture.UnmockAndWait<IConsumptionHandler, bool, RabbitMessage, CancellationToken>(
                fixture.ConsumptionHandlerMock,
                src => src.HandleMessageAsync(It.IsAny<RabbitMessage>(), It.IsAny<CancellationToken>()),
                cts);

            await fixture.ApplicationFactory.PublishJsonToRabbitAndWaitForConsumption("consumptionTest", "RabbitMessage", contract, cts);

            // This should be asserted against db or handler should create new notification etc
            // This static object is just here for test purposes where i don't want to include db or smth
            // but it is filled in handler where message is handled
            StaticRabbitHandlerState.RabbitMessage.BirthDate.Should().Be(contract.BirthDate);
            StaticRabbitHandlerState.RabbitMessage.Email.Should().Be(contract.Email);
            StaticRabbitHandlerState.RabbitMessage.SurName.Should().Be(contract.SurName);
            StaticRabbitHandlerState.RabbitMessage.Name.Should().Be(contract.Name);
        }
    }
}