Search code examples
c#unit-testingasp.net-coreintegration-testingasp.net-core-2.2

AspNetCore Integration Testing Multiple WebApplicationFactory Instances?


Does any one know if it is possible to host multiple instances of WebApplicationFactory<TStartup>() in the same unit test?

I have tried and can't seem to get anywhere with this one issue.

i.e

_client = WebHost<Startup>.GetFactory().CreateClient();
var baseUri = PathString.FromUriComponent(_client.BaseAddress);
_url = baseUri.Value;

_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer", "Y2E890F4-E9AE-468D-8294-6164C59B099Y");

WebHost is just a helper class that allows me to build factory and then a client easily in one line.

Under the covers all it does is this:

new WebApplicationFactory<TStartup>() but a few other things too.

It would be nice if i could stand up another instace of a different web server to test server to server functionality.

Does anyone know if this is possible or not?


Solution

  • Contrary to what the accepted answer states, it is actually pretty easy to test server to server functionality using two WebApplicationFactory instances:

    public class OrderAPIFactory : WebApplicationFactory<Order>
    {
        public OrderAPIFactory() { ... }
        protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
    }
    
    public class BasketAPIFactory : WebApplicationFactory<BasketStartup>
    {
        public BasketAPIFactory() { ... }
        protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
    }
    

    Then you can instantiate the custom factories as follows:

    [Fact] 
    public async Task TestName()
    {
        var orderFactory = new OrderAPIFactory();
        var basketFactory = new BasketAPIFactory();
    
        var orderHttpClient = orderFactory.CreateClient();
        var basketHttpClient = basketFactory.CreateClient();
    
        // you can hit eg an endpoint on either side that triggers server-to-server communication
        var orderResponse = await orderHttpClient.GetAsync("api/orders");
        var basketResponse = await basketHttpClient.GetAsync("api/basket");
    }
    

    I also disagree with the accepted answer about it necessarily being bad design: it has its use-cases. My company has a microservices infrastructure which relies on data duplication across microservices and uses an async messaging queue with integration events to ensure data consistency. Needless to say that messaging functionality plays a central role and needs to be tested properly. A test setup as described here is pretty useful in this situation. For example it allows us to thoroughly test how messages are being dealt with by a service that was down at the moment those messages were published:

    [Fact] 
    public async Task DataConsistencyEvents_DependentServiceIsDown_SynchronisesDataWhenUp()
    {
        var orderFactory = new OrderAPIFactory();
        var orderHttpClient = orderFactory.CreateClient();
    
        // a new order is created which leads to a data consistency event being published,
        // which is to be consumed by the BasketAPI service 
        var order = new Order { ... };
        await orderHttpClient.PostAsync("api/orders", order);
    
        // we only instantiate the BasketAPI service after the creation of the order
        // to mimic downtime. If all goes well, it will still receive the 
        // message that was delivered to its queue and data consistency is preserved
        var basketFactory = new BasketAPIFactory();
        var basketHttpClient = orderFactory.CreateClient();
    
        // get the basket with all ordered items included from BasketAPI
        var basketResponse = await basketHttpClient.GetAsync("api/baskets?include=orders");
        // check if the new order is contained in the payload of BasketAPI
        AssertContainsNewOrder(basketResponse, order); 
    }