
Faking Dependencies in Functional testing in ASP.NET WEB API
Author - Abdul Rahman (Bhai)
Web API
23 Articles
Table of Contents
What we gonna do?
In this article, let's learn about how to fake dependencies in Functional Test in WebAPI in ASP.NET Core.
Note: If you have not done so already, I recommend you read the article on Functional testing your ASP.NET WEB API.
There are situations where we need to fake dependencies or replace services in our Functional Test. We'll learn about where to place the boundary for integration tests and how this relates to the external dependencies, such as a database, queues, etc. And we'll create a fake for a dependency at that defined boundary.
Why we gonna do?
Rather than using services from our real cloud provider in this article, which would complicate the setup to follow along, it's good enough to use our imagination. When working with cloud providers, it's common to use language-specific SDKs that they supply and maintain to code against their managed services. The implementations just hold the data in memory to mimic a real service, and that's sufficient for this scenario.
The benefits of faking in functional tests include:
- Savings in cost.
- Faster time to complete the test.
- Simple & Easy to setup.
Therefore, lets fake these kind of dependencies.
How we gonna do?
Let's begin by creating a new test method named GetWeatherForecastFromDatabase, and we'll copy previous test to send the request and deserialize the response. So at this point, running the test results in a failure. Before we complete this test and its implementation, let's review the architecture of the API. We're building a cloud-native web API, so let's imagine that we're going to use some cloud services such as a managed database.
using API;
using FluentAssertions;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using Xunit.Abstractions;
namespace FunctionalTest;
[ExcludeFromCodeCoverage]
public class WeatherForecastControllerShouldTests(
CustomWebApiFactory factory,
ITestOutputHelper outputHelper) : BaseTest(factory), IClassFixture<CustomWebApiFactory>
{
[Fact]
public async Task ReturnExpectedResponse_From_Database()
{
var result = await Client.GetJsonResultAsync<List<WeatherForecast>>("/weatherforecast/fromdatabase", HttpStatusCode.OK, outputHelper);
result.Count.Should().Be(1);
result[0].Summary.Should().Be("Freezing from database");
}
}

We have WeatherForecastDbContext class in our API project. This is configured with cloud connection string and registed in Program.cs. We are going to inject this into our action method and read the weather forecast data from database. I'm going to get into details of setting up DbContext. I'll cover this in my EntityFramework Learning Path.


Faking Database
In this example in our API, we are going to use EntityFramework to connect to cloud database. We can fake the database by using an in-memory sqlite database provider to run our tests. Reason to use in memory sqlite provider than in memory database is to make sure relation constraints are working as expected.
Add reference to Microsoft.EntityFrameworkCore.Sqlite and Microsoft.Data.Sqlite Nuget Package in FunctionalTest.csproj.
Add static DatabaseHelper.cs helper class to Initialize and Reset Data for used in tests.
using API; using API.DbContexts; namespace FunctionalTest; public static class DatabaseHelper { public static async Task InitializeDbForTestsAsync(WeatherForecastDbContext db) { await db.WeatherForecasts.AddAsync( new WeatherForecast { Id = 1, Date = DateOnly.FromDateTime(DateTime.Now.AddDays(1)), TemperatureC = 1, Summary = "Freezing from database" }); await db.SaveChangesAsync(); } public static async Task ResetDbForTestsAsync(WeatherForecastDbContext db) { db.WeatherForecasts.RemoveRange(db.WeatherForecasts); await db.SaveChangesAsync(); } }Now we need to setup IAsyncLifetime using SharedFixture to Initialize database only once for all tests. This is shown in the below code.
using API.DbContexts; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace FunctionalTest; public class SharedFixture : IAsyncLifetime { public const string DatabaseName = "InMemoryTestDb"; public WeatherForecastDbContext DbContext = default!; public async Task InitializeAsync() { // Sqlite var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = DatabaseName, Mode = SqliteOpenMode.Memory, Cache = SqliteCacheMode.Shared }; var connection = new SqliteConnection(connectionStringBuilder.ToString()); var dbContextOptions = new DbContextOptionsBuilder<WeatherForecastDbContext>() .UseSqlite(connection) .Options; DbContext = new WeatherForecastDbContext(dbContextOptions); try { await DbContext.Database.EnsureDeletedAsync(); await DbContext.Database.OpenConnectionAsync(); await DbContext.Database.EnsureCreatedAsync(); await DatabaseHelper.ResetDbForTestsAsync(DbContext); await DatabaseHelper.InitializeDbForTestsAsync(DbContext); } catch (Exception) { throw; } } public async Task DisposeAsync() { if (DbContext is not null) { await DbContext.DisposeAsync(); } } }Next we need to setup CollectionDefinition using ICollectionFixture<TFixture> to make sure our fixture is initialized only once just before running first test in collection. This is shown in the below code.
namespace FunctionalTest; [CollectionDefinition(nameof(FunctionalTestCollection))] public class FunctionalTestCollection : ICollectionFixture<SharedFixture> { // This class has no code, and is never created. Its purpose is simply to be the place // to apply [CollectionDefinition] and all the ICollectionFixture<> interfaces. }Finally we need to configure in memory sqlite db in ConfigureServices inside ConfigureWebHost to override the Service Registration in CustomWebApiFactory from Program.cs.
We need to find WeatherForecastDbContext and remove and then create a sqlite in-memory connection and re-register WeatherForecastDbContext with the sqlite in-memory connection. We can inject SharedFixture to get Sqlite in memory database connection string. This is shown in the below code.
using API.DbContexts; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; namespace FunctionalTest; public class CustomWebApiFactory(SharedFixture fixture) : WebApplicationFactory<Program> { public SharedFixture SharedFixture => fixture; protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.UseEnvironment("Test"); builder.ConfigureServices(services => { var ctx = services.SingleOrDefault(d => d.ServiceType == typeof(WeatherForecastDbContext)); services.Remove(ctx!); var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = SharedFixture.DatabaseName, Mode = SqliteOpenMode.Memory, Cache = SqliteCacheMode.Shared }; var connection = new SqliteConnection(connectionStringBuilder.ToString()); // SQLite services.AddDbContext<WeatherForecastDbContext>(opts => opts.UseSqlite(connectionStringBuilder.ToString())); }); } }
Thats it. We are done with setup. Now the tests will run in the following flow.
- Start Test Method.
- Create WebApplicationFactory.
- Run Program.cs.
- Service Registration.
- Overriding Service Registration with ConfigureServices.
- Build Web Application.
- Create HTTP Client.
- Test Code Execution.
Before we run the test, we need to modify BastTest to expose DatabaseContext to test methods to assert values from Database with API result. This is shown in the below code.
using API.DbContexts;
namespace FunctionalTest;
public abstract class BaseTest(CustomWebApiFactory factory)
{
protected CustomWebApiFactory Factory => factory;
protected HttpClient Client => factory.CreateClient();
protected WeatherForecastDbContext Database => factory.SharedFixture.DbContext;
}
Now we need to decorate the test class with [Collection(nameof(FunctionalTestCollection))] and modify the test as shown below to get values from database and assert it with API result as shown below. Lets run the test and see the result.
using API;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using System.Diagnostics.CodeAnalysis;
using System.Net;
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_Database()
{
var expectedWeatherForecast = await Database.WeatherForecasts.FirstAsync();
var result = await Client.GetJsonResultAsync<List<WeatherForecast>>("/weatherforecast/fromdatabase", HttpStatusCode.OK, outputHelper);
result.Count.Should().Be(1);
result[0].Should().BeEquivalentTo(expectedWeatherForecast);
}
}

Replacing Services
Now that we learnt how to fake database. We can also apply the same technique to replace services in our Functional Test. Let's say we have a ExternalAPIService service in our API which implements IExternalAPIService. We can replace this service with a FakeExternalAPIService service in our Functional Test.
All we need to do is to find the ExternalAPIService and remove and then create a FakeExternalAPIService with expected output from that service and re-register ExternalAPIService with FakeExternalAPIService. This is shown in the below code.
But this way of replacing database or services will be difficult to maintain as we need to write a lot of code as replacement service for each scenario and test all scenarios. So I'll teach you another better way in next article.
Summary
In this article, we learnt about how to fake dependencies in Functional Test in WebAPI in ASP.NET Core. We learnt about where to place the boundary for integration tests and how this relates to the external dependencies, such as a database, queues, etc. This idea can be extended and applied to any cloud based dependencies. In our next article, we'll learn about how to use TestContainers in Functional Test in WebAPI in ASP.NET Core.