4

I'm using ASP.NET Core (2.0) with Autofac, and a Microsoft.AspNetCore.TestHost.TestServer for integration testing. However, for some test scenarios, I would like to inject some service mocks instead of the implementations loaded in ConfigureContainer method (as described here: http://docs.autofac.org/en/latest/integration/aspnetcore.html#quick-start-with-configurecontainer).

Example:

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .ConfigureServices(s => s.AddAutofac())
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

public class Startup
{
    ...
    public void ConfigureContainer(ContainerBuilder builder)
    {
        builder.RegisterModule(new Modules.ApiModule());
    }
    ...
}

And the test class:

public class BasicControllerTests
{
    TestServer server;
    HttpClient client;

    public BasicControllerTests()
    {
        var resellerRepo = new Mock<IResellerProvider>();
        resellerRepo.Setup(a => a.Query())
            .Returns(new[] {
            new Model.Reseller
            {
                Id = Guid.NewGuid(),
                Code = "R1",
                Name = "Reseller 1"
            }
            }.AsQueryable());

        // How to inject mock properly in the lines below?

        server = new TestServer(new WebHostBuilder()
            .ConfigureServices(a => a.AddAutofac())
            .UseStartup<Startup>());

        client = server.CreateClient();
    }
   ...

What I would like to do is to use the TestServer with all the dependencies as they are, but just the IResellerProvider mocked as in the test example. What is the best way to accomplish that? Of course, I could create a TestStartup class for this exact case, but I would like to know what is the proper way to handle this situation.

itim
  • 312
  • 2
  • 11
  • Create a parent generic class to setup all the providers, expressions .. and make your tests inherit it. – Ahmed Aug 24 '17 at 10:40
  • Last service registered overrides any previous registration. So let the normal service configuration go to completion and then configure your mocks – Nkosi Aug 24 '17 at 11:06
  • @AhmedRagheb: I would like to avoid that, since for various scenarios, I would potentially end up with many startup classess, – itim Aug 24 '17 at 11:08
  • @Nkosi Yes, my question is how do I do that? After I do `ConfigureServices` on a TestServer, how can I register more things? – itim Aug 24 '17 at 11:09
  • @itim why yo will end up with many statups classes ? – Ahmed Aug 24 '17 at 11:11
  • 1
    @Item you do them within that function ie `.ConfigureServices(services => { services.AddAutofac(); services.AddTransient(p => resellerRepo.Object); })` – Nkosi Aug 24 '17 at 11:11
  • I would still advise using a test startup which would be simpler – Nkosi Aug 24 '17 at 11:19
  • 1
    Yes, it would, but I can imagine that (for integration tests), for some scenarios I want to use a mock for just one thing, for the others a mock for another thing, and so on. I was wondering if there's any way to use Autofac itself (so I can also use, say, PropertiesAutowired, etc). Apart from that, this solution is fine, I would vote it up if you offer it as an answer. EDIT: However, it doesn't seem to work :( The service resolved is still the one defined in `ConfigureContainer`. – itim Aug 24 '17 at 11:49
  • make the `ConfigureContainer` virtual and override it in a derived class and use that class as startup. – Nkosi Aug 24 '17 at 11:54
  • This is similar https://stackoverflow.com/questions/54107886/mocking-and-resolving-autofac-dependency-in-integration-test-in-aspnetcore-with – diegosasw Jan 09 '19 at 12:53
  • Isn't the `.ConfigureTestServices(services => { })` method on WebHostBuilder supposed to deal with it? It doesn't work on my case and that's why I would like to know whether you accomplished it. Please provide details – diegosasw Jan 09 '19 at 13:05

1 Answers1

3

I found a workaround that works just fine and lets you inject any other dependency in .net core api. You will have this standard code to start up in your tests

var clientFactory = new WebApplicationFactory<Startup>();
var client = clientFactory.WithWebHostBuilder(builder =>
builder.ConfigureTestServices(services =>
{

}));

var _httpClient = client.CreateClient();

Now you need to pass to .ConfigureTestServices an Action and you can use this to remove the registration you have on the normal app startup and add another one that lets say it's a fake one.This in possible because if you look with debugger on services you will see that all those you registered are presend and you will just need to replace the ones you want to moke.Here is a simple example that I used

RemoveVehicleServiceRegistrationFrom(services);
services.AddScoped<IVehicleService, FakeVehicleService>();

In the Remove method you just need to find and remove old registration.Something like this

private static void RemoveVehicleServiceRegistrationFrom(IServiceCollection services)
{
     var vehicleService = services.Single(x => x.ServiceType == typeof(IVehicleService));
     services.Remove(vehicleService);
}

Final version looks like this

private HttpClient _httpClient;

[OneTimeSetUp]
public void Setup()
{
    var clientFactory = new WebApplicationFactory<Startup>();
    var client = clientFactory.WithWebHostBuilder(builder =>
        builder.ConfigureTestServices(services =>
        {
            RemoveVehicleServiceRegistrationFrom(services);
            services.AddScoped<IVehicleService, FakeVehicleService>();
        }));

    var _httpClient = client.CreateClient();
}

private static void RemoveVehicleServiceRegistrationFrom(IServiceCollection services)
{
    var vehicleService = services.Single(x => x.ServiceType == typeof(IVehicleService));
    services.Remove(vehicleService);
}
TheMyrel
  • 54
  • 4