mirror of https://github.com/abpframework/abp.git
3 changed files with 209 additions and 0 deletions
@ -0,0 +1,209 @@ |
|||
# Using Testcontainers in ABP Unit Test |
|||
|
|||
## What is Testcontainers? |
|||
|
|||
Testcontainers is a library that provides easy and lightweight APIs for bootstrapping local development and test dependencies with real services wrapped in Docker containers. |
|||
|
|||
Using Testcontainers, you can write tests that depend on the same services you use in production without mocks or in-memory services. |
|||
|
|||
Get more information about Testcontainers from [https://testcontainers.com/](https://testcontainers.com/). |
|||
|
|||
## How to Use Testcontainers in ABP Unit Test? |
|||
|
|||
ABP Framework provides a built-in unit test infrastructure, which easily allows you to add your unit tests and integration tests. |
|||
|
|||
It use [SQLite in-memory](https://learn.microsoft.com/en-us/ef/core/testing/testing-without-the-database#sqlite-in-memory) and [EphemeralMongo](https://github.com/asimmon/ephemeral-mongo) as the default database for unit tests and it's enough for most of the cases. However, sometimes you may need to test your code with a real database like PostgreSQL, MySQL, SQL Server, etc. |
|||
|
|||
In this article, I will show you how to use Testcontainers in ABP unit tests to test your code with a real database from a Docker container. |
|||
|
|||
> The Testcontainers will pull the Docker images of the databases you want to use. You can pull them manually before running the tests to speed up the tests. |
|||
|
|||
```bash |
|||
docker pull mcr.microsoft.com/mssql/server:2019-CU18-ubuntu-20.04 |
|||
docker pull mongo:6.0 |
|||
``` |
|||
|
|||
### Code Changes For Entity Framework Core Tests |
|||
|
|||
1. Remove `Volo.Abp.EntityFrameworkCore.Sqlite` package and add the `Testcontainers.MsSql` package to the `MyProjectName.EntityFrameworkCore.Tests` project. |
|||
|
|||
```csharp |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.EntityFrameworkCore\MyCompanyName.MyProjectName.EntityFrameworkCore.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> |
|||
<PackageReference Include="Testcontainers.MsSql" Version="3.7.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
``` |
|||
|
|||
2. Update `MyProjectNameEntityFrameworkCoreFixture` class as shown below: |
|||
|
|||
We start a SQL Server container in the `InitializeAsync` method and dispose of it in the `DisposeAsync` method. The `GetRandomConnectionString` method sets a random database for each test. |
|||
|
|||
```csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Testcontainers.MsSql; |
|||
using Xunit; |
|||
|
|||
namespace MyCompanyName.MyProjectName.EntityFrameworkCore; |
|||
|
|||
public class MyProjectNameEntityFrameworkCoreFixture : IAsyncLifetime |
|||
{ |
|||
private readonly static MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build(); |
|||
|
|||
public async Task InitializeAsync() |
|||
{ |
|||
await _msSqlContainer.StartAsync(); |
|||
} |
|||
|
|||
public static string GetRandomConnectionString() |
|||
{ |
|||
var randomDbName = "Database=Db_" + Guid.NewGuid().ToString("N"); |
|||
return _msSqlContainer.GetConnectionString().Replace("Database=master", randomDbName, StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
public async Task DisposeAsync() |
|||
{ |
|||
await _msSqlContainer.DisposeAsync().AsTask(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
3. Update `MyProjectNameEntityFrameworkCoreTestModule` class as shown below: |
|||
|
|||
```csharp |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
using Volo.Abp.FeatureManagement; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.PermissionManagement; |
|||
using Volo.Abp.SettingManagement; |
|||
using Volo.Abp.Uow; |
|||
|
|||
namespace MyCompanyName.MyProjectName.EntityFrameworkCore; |
|||
|
|||
[DependsOn( |
|||
typeof(MyProjectNameApplicationTestModule), |
|||
typeof(MyProjectNameEntityFrameworkCoreModule) |
|||
)] |
|||
public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
ConfigureMsSqlDatabase(context.Services); |
|||
} |
|||
|
|||
private void ConfigureMsSqlDatabase(IServiceCollection services) |
|||
{ |
|||
var connectionString = MyProjectNameEntityFrameworkCoreFixture.GetRandomConnectionString(); |
|||
using (var context = new MyProjectNameDbContext(new DbContextOptionsBuilder<MyProjectNameDbContext>() |
|||
.UseSqlServer(connectionString) |
|||
.Options)) |
|||
{ |
|||
context.Database.Migrate(); |
|||
} |
|||
services.Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.Configure(context => |
|||
{ |
|||
context.DbContextOptions.UseSqlServer(connectionString); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The EF Core unit tests results will be like the following: |
|||
|
|||
 |
|||
|
|||
### Code Changes For MongoDB Tests |
|||
|
|||
1. Remove `EphemeralMongo` related packagesand add the `Testcontainers.MongoDb` package to the `MyProjectName.EntityFrameworkCore.Tests` project. |
|||
|
|||
```csharp |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net8.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace>MyCompanyName.MyProjectName</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj" /> |
|||
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" /> |
|||
<PackageReference Include="Testcontainers.MongoDb" Version="3.7.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
``` |
|||
|
|||
2. Update `MyProjectNameMongoDbFixture` class as shown below: |
|||
|
|||
We start a MongoDb container in the `InitializeAsync` method and dispose of it in the `DisposeAsync` method. The `GetRandomConnectionString` method sets a random database for each test. |
|||
|
|||
```csharp |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Testcontainers.MongoDb; |
|||
using Xunit; |
|||
|
|||
namespace MyCompanyName.MyProjectName.MongoDB; |
|||
|
|||
public class MyProjectNameMongoDbFixture : IAsyncLifetime |
|||
{ |
|||
private readonly static MongoDbContainer _mongoDbContainer = new MongoDbBuilder().WithCommand().Build(); |
|||
|
|||
public async Task InitializeAsync() |
|||
{ |
|||
await _mongoDbContainer.StartAsync(); |
|||
} |
|||
|
|||
public static string GetRandomConnectionString() |
|||
{ |
|||
var randomDbName = "Db_" + Guid.NewGuid().ToString("N"); |
|||
return _mongoDbContainer.GetConnectionString().EnsureEndsWith('/') + randomDbName + "?authSource=admin"; |
|||
} |
|||
|
|||
public async Task DisposeAsync() |
|||
{ |
|||
await _mongoDbContainer.DisposeAsync().AsTask(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The MongoDb unit tests results will be like the following: |
|||
|
|||
 |
|||
|
|||
## Summary |
|||
|
|||
The Testcontainers works well with ABP Framework and it's easy to use. If you need to test your code with a real database, Testcontainers is a good choice for you. |
|||
|
|||
It's still a little slow compared to in-memory databases. Of course, its advantages are obvious. |
|||
|
|||
Enjoy testing with Testcontainers! |
|||
|
After Width: | Height: | Size: 687 KiB |
|
After Width: | Height: | Size: 686 KiB |
Loading…
Reference in new issue