Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace service with mocked using WebApplicationFactory

I'm trying to replace an implementation of IMessageFormatter with a MockMessageFormatter for a test.

public class MyMockingWebApplicationFactory<TStartUp> : WebApplicationFactory<Startup>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureServices(services => { services.AddTransient<IMessageFormatter, MockMessageFormatter>(); });
    }
}

The test looks like so:

public class MockingTests : IClassFixture<MyMockingWebApplicationFactory<Startup>>
{
    public MockingTests(MyMockingWebApplicationFactory<Startup> webApplicationFactory)
    {
        _webApplicationFactory = webApplicationFactory;
    }

    private readonly MyMockingWebApplicationFactory<Startup> _webApplicationFactory;

    [Fact]
    public async Task MockIt()
    {
        var client = _webApplicationFactory.WithWebHostBuilder(builder =>
        {
            //builder.ConfigureServices(services =>
            //{
            //    var foo = ServiceDescriptor.Transient<IMessageFormatter, MessageFormatter>();
            //    services.Remove(foo);
            //    services.AddTransient<IMessageFormatter, MockMessageFormatter>();
            //});
        }).CreateClient();

        var message = "Hello";
        var expectedReversedMessage = "foo";
        var result = await client.GetAsync($"/?message={message}");
        var content = await result.Content.ReadAsStreamAsync();
        var htmlParser = new HtmlParser();
        var htmlDocument = await htmlParser.ParseAsync(content);
        var messageElement = htmlDocument.GetElementById("original-message");
        var reversedMessageElement = htmlDocument.GetElementById("reversed-message");
        Assert.Equal(message, messageElement.InnerHtml);
        var reversedMessage = reversedMessageElement.InnerHtml;
        Assert.Equal(expectedReversedMessage, reversedMessage);
    }
}

I've tried adding the call to ConfigureServices in the client and in the MyMockingWebApplicationFactory but the problem seems to be that the real Startup class is executed after the test's registration and so overwrites them.

The real StartUp class is:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); ;
        services.AddTransient<IMessageFormatter, MessageFormatter>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseDeveloperExceptionPage();
        app.UseMvc();
    }
}
like image 852
BanksySan Avatar asked Nov 27 '18 20:11

BanksySan


1 Answers

replace your code :

builder.ConfigureServices(services => { services.AddTransient<IMessageFormatter, MockMessageFormatter>(); });

to:

builder.ConfigureTestServices(services =>
        {
            var serviceProvider = services.BuildServiceProvider();
            var descriptor =
                new ServiceDescriptor(
                    typeof(IMessageFormatter),
                    typeof(MockMessageFormatter),
                    ServiceLifetime.Transient);
            services.Replace(descriptor);
        });
like image 133
yayayahei Avatar answered Oct 25 '22 20:10

yayayahei