Search code examples
c#asp.net-coreintegration-testing

Can't pass mocked services to ConfigureTestServices in .NET Core API Integration test


I use minimal hosting API using .NET Core 7 with "flat" Program.cs such as:

var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
var services = builder.Services;
services.AddControllers();
services.AddSingleton<IBla, Bla>();
services.AddSingleton<IFoo, Foo>();
// etc..
var app = builder.Build();
// etc..
app.Run();

public partial class Program { }

IFoo is injected in Bla constructor. Then in a Nunit test (along with FakeItEasy), there is:

var application = new WebApplicationFactory<Program>().WithWebHostBuilder(builder => {
        builder.UseEnvironment("Development")
                .ConfigureTestServices(services => {
   
                    // register the service being tested
                    // Do I need it as it's already in Program.cs?
                    services.AddScoped<IBla, Bla>();


                    // WORKS
                    services.AddScoped<IFoo, FooFake>();

                    // DOES NOT WORK
                    //var foo = new Fake<IFoo>();
                    //foo.CallsTo(x => x.GetAsync(A<string>.Ignored))
                    //                .Returns(111);
                    //services.AddSingleton(foo);
                });
        });


var client = application.CreateClient();
var response = await client.GetAsync("/api/car/21");

When I use mocked service, the real implementation is invoked (the one from Program.cs) When I use Fake service it works fine but that's not what I want.

Thank you in advance for your help!


Solution

  • Do I need it as it's already in Program.cs?

    No, you don't if the registered implementation satisfies you

    WORKS

    Yes, this will work, build-in DI uses the last registered implementation, with a caveat - if you will resolve IEnumerable<IFoo> somewhere it will contain the "unwanted" implementation too. Usual approach to mitigate that is to clean up the service collection before adding new registration:

    services.RemoveAll(typeof(IFoo)); 
    

    DOES NOT WORK

    Because services.AddSingleton(foo); will register service of type Fake<IFoo>. Not sure what Fake is but possibly you want something like services.AddSingleton<IFoo>(foo); or services.AddSingleton<IFoo>(foo.SomethingWhatReturnsFoo); depending on how this particular fake/mock library works.