dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.28k stars 9.96k forks source link

WebApplicationFactory failing to replace registrations in Background Service #55845

Open Pastafarian opened 4 months ago

Pastafarian commented 4 months ago

Is there an existing issue for this?

Describe the bug

I have run into an issue when trying to integration test a background service that listens to messages from RabbitMq. The issue is, after the first test runs, the subsequent test still has the same registration as the previous one.

I have created a project to illustrate the issue.

 [Fact]
        public async Task FirstTest1()
        {
            await SetupRabbitMq();

            _fixture.BuildConsumerHttpClient((service) =>
            {
                service.Replace(ServiceDescriptor.Transient<IMessage, MessageTest1>());
                return true;
            });

            BasicPublish(new PublicationAddress(_rabbitMqClientConsumerConfiguration.ExchangeType, _rabbitMqClientConsumerConfiguration.LoggingExchangeName, string.Empty), "MessageTest1"u8.ToArray());
        }

        [Fact]
        public async Task SecondTest2()
        {
            await SetupRabbitMq();

            _fixture.BuildConsumerHttpClient((service) =>
            {
                service.Replace(ServiceDescriptor.Transient<IMessage, MessageTest2>());

                return true;
            });

            BasicPublish(new PublicationAddress(_rabbitMqClientConsumerConfiguration.ExchangeType, _rabbitMqClientConsumerConfiguration.LoggingExchangeName, string.Empty), "MessageTest2"u8.ToArray());
        }

public class MessageTest1 : IMessage
{
    public string Message => "MessageTest1";
}
public class MessageTest2 : IMessage
{
    public string Message => "MessageTest2";
}

Here we set the service for each test to be MessageTest1 and MessageTest2 respectively along with the message that is sent from each test. Yet, as we can see in the Service, while it is receiving the message string "MessageTest2", the service registered is MessageTest1.

image

The tests use testcontainers to allow for easy running.

Expected Behavior

No response

Steps To Reproduce

https://github.com/Pastafarian/WebApplicationFactoryBugIssue

Exceptions (if any)

No response

.NET Version

8

Anything else?

No response

martincostello commented 4 months ago

Quickly glancing at the code, it's possible that your background service has already been instantiated and is running in the web host by the time your tests run, so it's too late for any changes to service registrations to take effect.

Pastafarian commented 4 months ago

Thanks for your suggestion @martincostello, even when I remove the service and add it again, I still get the same problem.

        [Fact]
        public async Task FirstTest1()
        {
            await SetupRabbitMq();

            _fixture.BuildConsumerHttpClient((service) =>
            {
                var descriptor = service.Single(s => s.ImplementationType == typeof(WorkerService));
                service.Remove(descriptor);
                service.AddHostedService<WorkerService>();
                service.Replace(ServiceDescriptor.Transient<IMessage, MessageTest1>());
                return true;
            });

            BasicPublish(new PublicationAddress(_rabbitMqClientConsumerConfiguration.ExchangeType, _rabbitMqClientConsumerConfiguration.LoggingExchangeName, string.Empty), "MessageTest1"u8.ToArray());
        }

Any suggestions on how I could fix this?

Pastafarian commented 3 months ago

@davidfowl any chance you'd be able to take a quick look?

Thanks!