diff --git a/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/POST.md b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/POST.md new file mode 100644 index 0000000000..dbb9db8f58 --- /dev/null +++ b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/POST.md @@ -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 + + + + + + net8.0 + enable + MyCompanyName.MyProjectName + + + + + + + + + + + + + +``` + +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() + .UseSqlServer(connectionString) + .Options)) + { + context.Database.Migrate(); + } + services.Configure(options => + { + options.Configure(context => + { + context.DbContextOptions.UseSqlServer(connectionString); + }); + }); + } +} +``` + +The EF Core unit tests results will be like the following: + +![ef core](efcore.png) + +### Code Changes For MongoDB Tests + +1. Remove `EphemeralMongo` related packagesand add the `Testcontainers.MongoDb` package to the `MyProjectName.EntityFrameworkCore.Tests` project. + +```csharp + + + + + + net8.0 + enable + MyCompanyName.MyProjectName + + + + + + + + + + + + + +``` + +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: + +![mongodb](mongodb.png) + +## 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! diff --git a/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/efcore.png b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/efcore.png new file mode 100644 index 0000000000..37b93c5e3e Binary files /dev/null and b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/efcore.png differ diff --git a/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/mongodb.png b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/mongodb.png new file mode 100644 index 0000000000..6223a8630b Binary files /dev/null and b/docs/en/Community-Articles/2024-03-02-Using-Testcontainers-In-ABP-Unit-Test/mongodb.png differ