-
Notifications
You must be signed in to change notification settings - Fork 3
Testing Strategy and Infrastructure
Oftentimes a developer would like to perform integration tests without permanently changing test data. There are many strategies that can accomplish this objective, including use of an in-memory database.
An in-memory database is a non-relational Entity Framework store that allows normal querying of DbSets and DbQuerys and "persists" changes to DbSets when the SaveChanges or SaveChangesAsync method is called. An in-memory database is generated (and populated with test data) before each test. To setup an automated test to use an in-memory database, the developer has some options, including:
- Create a new DbContext using an in-memory provider within the test method itself (see Testing with InMemory - EF Core). This approach is helpful if the developer wishes to test Entity-Framework-related code without going through a controller.
- Create a custom WebApplicationFactory that manually overrides the DbContext configurations in the target project's startup class with an in-memory configuration (see Integration tests in ASP.NET Core).
- Within the ConfigureServices method of the Startup class, setup a HostingEnvironment-equals-Development control block that configures a DbContext to use an in-memory provider. For example,
// requires injecting IHostingEnvironment into Startup Constructor and saving as variable or property
if (HostingEnvironment.EnvironmentName == EnvironmentName.Development) {
services.AddDbContext<MyDbContext>(options => {
options.UseInMemoryDatabase("MyInMemoryDatabase");
});
}The third strategy presented above has three significant advantages over the other two strategies. First, it requires much less code in the integration tests. Second, it allows the developer to use in-memory databases for spot-testing outside of automated unit tests. Third, the same exact configuration would work regardless of how many levels of APIs are called before the database is involved. With the first two methods, the developer must either directly instantiate the DbContext or setup dependency injection for the DbContext in the web application being tested. How would the developer setup dependency injection in an API that was called by the web application being tested? Also, if an integration test involved calls to multiple endpoints (e.g., a POST and then a GET), how would the developer be able to implement the test?
On the other hand, there is at least one significant disadvantage to the third strategy -- namely, that because the database name is a constant value, it is possible that multiple tests running asynchronously in separate threads might not be truly independent. With the first two strategies, the developer could use different database names for each test, thereby avoiding any possible collisions among test execution threads.
One major objective of the EDennis.AspNetCore.Base libraries was to make integration testing and spot testing simple, consistent, and thread safe, no matter how many levels of APIs are called before Entity Framework operations are involved. The library makes use of Dictionary-based singletons for caching DbContexts in the Development environment, special HTTP request headers for telling APIs which cached context to use for a given request, and interceptors (filters) for either forwarding the special HTTP headers (when multiple levels of APIs are present) or updating the DbContext property of a repository class. The library also includes some Xunit testing base classes for simplifying the construction of test classes.
(to be continued)
(to be continued)
(to be continued)
EDennis.AspNetCore.Base provides several base classes that simplify the creation of Xunit integration tests that involve an in-memory database. These classes are described in a separate Wiki page (Xunit Base Classes).
Development Support
- Temporal Entities
- Base Repository Classes
- Base API Controller Classes
- ApiClient and SecureApiClient
- Security Utilities
- IServiceCollection Extension Methods
- HttpClient Extension Methods
- ScopeProperties
- MigrationsExtensionsDbContextDesignTimeFactory
Testing Support
- API Launcher
- Testing Strategy and Infrastructure
- Xunit Support Classes
- Testing Security Utilities
Related Projects