docs/en/Community-Articles/2024-12-09-Unit-Test/POST.md
A typical ABP modular project usually consists of three main projects: Application, Domain, and EntityFrameworkCore/MongoDB. In these projects, we may provide many services that require unit testing.
Using abstract unit test classes involves first writing tests in the Application and Domain layers that are independent of the storage technology, ensuring the correctness of core business logic. These abstract tests are then implemented in EntityFrameworkCore or MongoDB. The benefits of this approach include:
Assume our project name is MyCompanyName.MyProjectName.
MyCompanyName.MyProjectName.Application.Tests project:MyCompanyName.MyProjectName.Application.Tests project's MyProjectNameApplicationCollection class.MyCompanyName.MyProjectName.Application.Tests project's MyProjectNameApplicationTestBase class.public abstract class MyProjectNameApplicationTestBase<TStartupModule> : MyProjectNameTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
//...
}
MyCompanyName.MyProjectName.Application.Tests project's unit test classes to become abstract unit test classes, such as: SampleAppServiceTests.public abstract class SampleAppServiceTests<TStartupModule> : MyProjectNameApplicationTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
[Fact]
public async Task Initial_Data_Should_Contain_Admin_User()
{
//...
}
}
MyCompanyName.MyProjectName.Domain.Tests project:MyCompanyName.MyProjectName.Domain.Tests project's MyProjectNameDomainCollection class.MyCompanyName.MyProjectName.Domain.Tests project's MyProjectNameDomainTestBase class.public abstract class MyProjectNameDomainTestBase<TStartupModule> : MyProjectNameTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
//...
}
MyCompanyName.MyProjectName.Domain.Tests project's unit test classes to become abstract unit test classes, such as: SampleDomainTests.public abstract class SampleDomainTests<TStartupModule> : MyProjectNameDomainTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
[Fact]
public async Task Should_Set_Email_Of_A_User()
{
//...
}
}
MyCompanyName.MyProjectName.Domain.Tests project's csproj and module class. Remove references to EntityFrameworkCore/MongoDB.MyCompanyName.MyProjectName.Domain.Tests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
//...
<ItemGroup>
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.Domain\MyCompanyName.MyProjectName.Domain.csproj" />
<ProjectReference Include="..\MyCompanyName.MyProjectName.TestBase\MyCompanyName.MyProjectName.TestBase.csproj" />
</ItemGroup>
</Project>
MyProjectNameDomainTestModule.cs:
[DependsOn(
typeof(MyProjectNameDomainModule),
typeof(MyProjectNameTestBaseModule)
)]
public class MyProjectNameDomainTestModule : AbpModule
{
//...
}
MyCompanyName.MyProjectName.EntityFrameworkCore.Tests project:Here, we need to create implementation classes for all abstract unit tests.
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)]
public class EfCoreSampleAppServiceTests : SampleAppServiceTests<MyProjectNameEntityFrameworkCoreTestModule>
{
//...
}
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)]
public class EfCoreSampleDomainTests : SampleDomainTests<MyProjectNameEntityFrameworkCoreTestModule>
{
//...
}
We also need to modify the project's dependencies and module class, which should directly or indirectly reference the Application and Domain test projects.
MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj:
<Project Sdk="Microsoft.NET.Sdk">
//...
<ItemGroup>
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.EntityFrameworkCore\MyCompanyName.MyProjectName.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.EntityFrameworkCore.Sqlite\Volo.Abp.EntityFrameworkCore.Sqlite.csproj" />
</ItemGroup>
</Project>
MyProjectNameEntityFrameworkCoreTestModule.cs:
[DependsOn(
typeof(MyProjectNameApplicationTestModule),
typeof(MyProjectNameEntityFrameworkCoreModule),
typeof(AbpEntityFrameworkCoreSqliteModule)
)]
public class MyProjectNameEntityFrameworkCoreTestModule : AbpModule
{
//...
}
MyCompanyName.MyProjectName.MongoDB.Tests project (skip this step if not using MongoDB):Like the EntityFrameworkCore project, we need to create implementation classes for all abstract unit tests and modify the project's dependencies and module class.
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)]
public class MongoDBSampleAppServiceTests : SampleAppServiceTests<MyProjectNameMongoDbTestModule>
{
//...
}
[Collection(MyProjectNameTestConsts.CollectionDefinitionName)]
public class MongoDBSampleDomainTests : SampleDomainTests<MyProjectNameMongoDbTestModule>
{
//...
}
<Project Sdk="Microsoft.NET.Sdk">
//...
<ItemGroup>
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.MongoDB\MyCompanyName.MyProjectName.MongoDB.csproj" />
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" />
</ItemGroup>
</Project>
[DependsOn(
typeof(MyProjectNameApplicationTestModule),
typeof(MyProjectNameMongoDbModule)
)]
public class MyProjectNameMongoDbTestModule : AbpModule
{
//...
}
MyCompanyName.MyProjectName.Web.Tests project:We need to reference the EntityFrameworkCore/MongoDB test projects in this test project.
<Project Sdk="Microsoft.NET.Sdk">
//...
<ItemGroup>
<ProjectReference Include="..\MyCompanyName.MyProjectName.Application.Tests\MyCompanyName.MyProjectName.Application.Tests.csproj" />
<ProjectReference Include="..\..\src\MyCompanyName.MyProjectName.Web\MyCompanyName.MyProjectName.Web.csproj" />
<ProjectReference Include="..\..\..\..\..\framework\src\Volo.Abp.AspNetCore.TestBase\Volo.Abp.AspNetCore.TestBase.csproj" />
<ProjectReference Include="..\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests\MyCompanyName.MyProjectName.EntityFrameworkCore.Tests.csproj" />
</ItemGroup>
</Project>
[DependsOn(
typeof(AbpAspNetCoreTestBaseModule),
typeof(MyProjectNameWebModule),
typeof(MyProjectNameApplicationTestModule),
typeof(MyProjectNameEntityFrameworkCoreTestModule)
)]
public class MyProjectNameWebTestModule : AbpModule
{
//...
}
We no longer need the MyProjectNameWebCollection class in this project. Please delete it and use [Collection(MyProjectNameTestConsts.CollectionDefinitionName)] instead.
This is our new unit test structure. Decoupling unit tests from storage technologies ensures the independence of business logic and allows easy switching between storage implementations. Abstract unit test classes improve test reusability, maintainability, and efficiency, reducing refactoring costs and providing flexibility for future tech updates.