👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Using WireMock.NET in Functional testing in ASP.NET WEB API

Using WireMock.NET in Functional testing in ASP.NET WEB API

Author - Abdul Rahman (Bhai)

Web API

23 Articles

Improve

In this article, let's learn about how to use WireMock.NET in Functional Test in WebAPI in ASP.NET Core.

Note: If you have not done so already, I recommend you read the article on Faking Dependencies in Functional testing in ASP.NET WEB API.

Table of Contents

  1. Introduction
  2. Why to use WireMock.NET?
  3. Implementing WireMock.NET
  4. Summary

Introduction

In our previous article we saw how to replace services in functional test and that approach has a drawback of writing lot of code to test all the possible scenarios especially if you are writing fake services for sake of tests. We can overcome this drawback by using WireMock.NET. It helps us to simplify the test code and also helps us to test all the possible scenarios. In this article, we'll learn about how to use WireMock.NET in functional test.

Let's start by adding Task<WeatherForecast> GetWeatherForecast() to our IExternalAPIService and implement it in ExternalAPIService. Before we dive into this implementation, lets first add URL and HttpClient configuration in our Program.cs.


{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "WeatherAPIUrl": "https://api.open-meteo.com/" // <-- External URL to be used to get weather information
}
        

Now in the Program.cs, lets register IExternalService with ExternalService add the HttpClient configuration as shown below.


builder.Services.AddTransient<IExternalAPIService, ExternalAPIService>();
builder.Services.AddHttpClient("WeatherClient", client =>
{
    client.BaseAddress = new Uri(builder.Configuration.GetValue<string>("WeatherAPIUrl")!);
    client.DefaultRequestHeaders.Clear();
    client.DefaultRequestHeaders.Add(HeaderNames.Accept, MediaTypeNames.Application.Json);
});
        

Now all we need to do is to inject the above WeatherClient HTTP Client inside our ExternalAPIService and make a call to get weather information as shown below.


using System.Text.Json.Serialization;

namespace API.Services;

public class ExternalAPIService : IExternalAPIService
{
    private readonly IHttpClientFactory httpClientFactory;

    public ExternalAPIService(IHttpClientFactory httpClientFactory)
    {
        this.httpClientFactory = httpClientFactory;
    }

    public async Task<WeatherForecast> GetWeatherForecast()
    {
        var client = httpClientFactory.CreateClient("WeatherClient");

        var response = await client.GetFromJsonAsync<ExternalWeatherForecast>("v1/forecast?latitude=52.52&longitude=13.41&current_weather=true");

        var weatherForecast = new WeatherForecast
        {
            Date = DateOnly.FromDateTime(DateTime.Now),
            TemperatureC = (int)response!.Current_Weather.Temperature
        };

        return weatherForecast;
    }
}

public class ExternalWeatherForecast
{
    [JsonPropertyName("current_weather")]
    public CurrentWeather Current_Weather { get; set; }
}

public class CurrentWeather
{
    [JsonPropertyName("temperature")]
    public decimal Temperature { get; set; }
}
        

Next we need to call the GetWeatherForecast method from our WeatherForecastController as shown below.

Endpoint to return values from External API from-api-endpoint

Here is the swagger response from the GetWeatherForecastFromAPI endpoint.

Swagger output from Endpoint to return values from API swagger-response

Why to use WireMock.NET ?

WireMock.NET is a powerful tool that can accurately emulate the behavior of an HTTP API by capturing incoming requests and directing them to a WireMock.NET HTTP server. This capability grants us the ability to define expectations, call the API, and verify its behavior, making it ideal for extensive testing of code interactions with external services.

With WireMock.NET, we can effectively mock real API endpoints and utilize its comprehensive features for HTTP response stubbing, including matching stubs based on URL/Path, headers, cookies, and body content patterns.

The benefits of using WireMock.NET in functional tests include:

  • No need to write lot of mock code for HTTP calls. WireMock.NET helps to test actual behavior.
  • WireMock.NET helps when External API calls have rate limitation.
  • Easy to setup and cover all possible status code behaviors.

Implementing WireMock.NET

We will be using the same project we used in the previous article and try adding WireMock.NET setup. The steps to implement WireMock.NET in functional test are as follows:

  1. Add reference to WireMock.NET Nuget Package in FunctionalTest.csproj.

  2. Setup SharedFixture as shown below

    
    using WireMock.Server;
    using WireMock.Settings;
    
    namespace FunctionalTest;
    public class SharedFixture : IAsyncLifetime
    {
        public WireMockServer WeatherService = default!;
    
        // WireMock for Weather Service --------------------
        public string WeatherServiceUrl { get; private set; } = null!;
    
        private string StartWireMockForWeatherService()
        {
            WeatherService = WireMockServer.Start(new WireMockServerSettings
            {
                UseSSL = false
            });
    
            return WeatherService.Urls[0];
        }
    
        public async Task InitializeAsync()
        {
            WeatherServiceUrl = StartWireMockForWeatherService();
        }
    
        public async Task DisposeAsync()
        {
            if (WeatherService is not null)
            {
                WeatherService.Stop();
                WeatherService.Dispose();
            }
        }
    }
                    

    In the above code, we are initializing the WireMockServer and getting the Server Base URL from the WireMockServer and exposing it to be used in ConfigureAppConfiguration in CustomWebApiFactory to override with this URL. We can also configure SSL, port, etc during initialization here. We are also disposing the WireMockServer after all tests are run.

  3. Now override the Configuration in WebHostBuilder inside CustomWebApiFactory in FunctionalTest.cs as shown below.

    
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc.Testing;
    using Microsoft.Extensions.Configuration;
    
    namespace FunctionalTest;
    public class CustomWebApiFactory(SharedFixture fixture) : WebApplicationFactory<Program>
    {
        public SharedFixture SharedFixture => fixture;
    
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.UseEnvironment("Test");
    
            builder.ConfigureAppConfiguration((context, conf) =>
            {
                conf.AddInMemoryCollection(new Dictionary<string, string>
                {
                    ["WeatherAPIUrl"] = $"{SharedFixture.WeatherServiceUrl}/"
                }!);
            });
        }
    }
                    
  4. Before we run the test, we need to modify BastTest to expose WireMockServer to setup HTTP response for ExtrenalAPI result. This is shown in the below code.
    
    using API.DbContexts;
    using WireMock.Server;
    
    namespace FunctionalTest;
    
    public abstract class BaseTest(CustomWebApiFactory factory)
    {
        protected CustomWebApiFactory Factory => factory;
        protected HttpClient Client => factory.CreateClient();
        protected WeatherForecastDbContext Database => factory.SharedFixture.DbContext;
        protected WireMockServer WeatherService => factory.SharedFixture.WeatherService;
    }
                    
  5. Now we need to setup response from the WireMock.NET server to be used in test. For that we need to add the following setup code as shown below
    
    var expectedWeather = new ExternalWeatherForecast { Current_Weather = new CurrentWeather { Temperature = 18 } };
    WeatherService
        .Given(Request.Create().WithPath("/v1/forecast").UsingGet())
        .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK).WithBodyAsJson(expectedWeather));
                    

    The above setup is responsible to do request matching particular Path in WireMock.NET server and also the response to be returned using BodyAsJson. With this setup when we call External API in Functional Test WireMock kicks in and when the path matches, it will give the expected response to be asserted in test.

    You can refer to the WireMock.NET documentation for more details on how to setup the request and response for various cases like timeouts, auth failure, etc.

  6. Now we are left with our test. So lets add the test as shown below.
    
    using API;
    using API.Services;
    using FluentAssertions;
    using System.Diagnostics.CodeAnalysis;
    using System.Net;
    using WireMock.RequestBuilders;
    using WireMock.ResponseBuilders;
    using Xunit.Abstractions;
    
    namespace FunctionalTest;
    [ExcludeFromCodeCoverage]
    [Collection(nameof(FunctionalTestCollection))]
    public class WeatherForecastControllerShouldTests(
        CustomWebApiFactory factory,
        ITestOutputHelper outputHelper) : BaseTest(factory), IClassFixture<CustomWebApiFactory>
    {
        [Fact]
        public async Task ReturnExpectedResponse_From_API()
        {
            var expectedWeather = new ExternalWeatherForecast { Current_Weather = new CurrentWeather { Temperature = 18 } };
            WeatherService
                .Given(Request.Create().WithPath("/v1/forecast").UsingGet())
                .RespondWith(Response.Create().WithStatusCode(HttpStatusCode.OK).WithBodyAsJson(expectedWeather));
    
            var result = await Client.GetJsonResultAsync<WeatherForecast>("/weatherforecast/fromapi", HttpStatusCode.OK, outputHelper);
            
            result.Should().NotBeNull();
            result.TemperatureC.Should().Be((int)expectedWeather.Current_Weather.Temperature);
            result.Date.Should().Be(DateOnly.FromDateTime(DateTime.Now));
        }
    }
                    

    In the above code if you notice, we are returning Temperature value as 18 from WireMockServer response and using it to assert in the test.

Thats it. We are done with setup. Now the tests will run in the following flow.

  1. Start Test Method.
  2. WireMockServer Setup.
  3. Create WebApplicationFactory.
  4. Run Program.cs.
  5. Service Registration.
  6. Overriding Configuration.
  7. Build Web Application.
  8. Create HTTP Client.
  9. Test Code Execution.

Now lets run the test and see the result.

passing-test

Summary

In this article, we learnt about how to use WireMock.NET in functional test in ASP.NET Web API. We also saw the benefits of using WireMock.NET in functional test. The complete source code for this article can be found here. Hope you find this information useful. In our next article we will continue to learn how to setup Authentication in Functional Test in ASP.NET Web API.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • Web API
  • WireMock.Net
  • Functional Test
  • Integration Test