Faking Dependencies in Functional testing in ASP.NET WEB API
WebAPI
19 Articles
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.
Table of Contents
Introduction
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.
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.
Code Sample - Test to validate data 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.
Why to Fake Dependencies ?
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.
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.
Code Sample - Database Helper
-
Now we need to setup IAsyncLifetime using SharedFixture to
Initialize database only once for all tests. This is shown in the below code.
Code Sample - Initialize Database only once using IAsyncLifeTime
-
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.
Code Sample - Initialize Fixture only once using ICollectionFixture<TFixture>
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.
Code Sample - Custom Web Application Factory
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.
Code Sample - Expose DbContext in BaseTest
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.
Code Sample - Decorate Test class with Collection attribure
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.