用途
EAF Testing 是一份用于测试 EAF(Enterprise Application Foundation)模块的技能指南。EAF 是构建在 ASP.NET Boilerplate(ABP)之上的开源中间件平台,测试对于保障代码质量、维持各模块约 90% 的覆盖率至关重要。该技能面向使用 xUnit、Shouldly 断言与 NSubstitute 模拟编写功能完整、可维护测试的开发者。
技术栈
- .NET 版本:10.0
- 测试框架:xUnit 2.4+
- 断言库:Shouldly 4.x
- 模拟库:NSubstitute 5.x
- 覆盖率:coverlet.collector 6.x
- ABP 测试:Abp.TestBase、Abp.EntityFramework.TestBase
测试项目结构
test/
├── Eaf.ModuleName.Tests/
│ ├── EafModuleNameTestModule.cs
│ ├── ModuleName/
│ │ ├── EntityName_Tests.cs
│ │ └── ServiceName_Tests.cs
│ └── Eaf.ModuleName.Tests.csproj
测试基类与测试模块
集成测试基类继承 AbpIntegratedTestBase<EafMyModuleTestModule>,可重写 PreInitialize 与 PostInitialize。测试模块通过 [DependsOn(typeof(EafMyModule), typeof(AbpTestBaseModule))] 声明依赖,并在 Initialize 中调用 IocManager.RegisterAssemblyByConvention 与 ServiceCollectionRegistrar.Register。
BDD 命名模式(葡萄牙语)
测试命名采用 BDD 模式:Dado_ParametroValido_Quando_ChamarMetodo_Entao_DeveRetornarSucesso,对应 Given/When/Then 三段结构。
主要测试类型
- 实体测试:验证实体创建与校验,例如无效邮箱应抛出
AbpValidationException。 - 应用服务测试:通过
Resolve<IUserAppService>()解析服务,测试创建用户等场景。 - 带模拟的服务测试:使用
Substitute.For<IRepository<User, Guid>>()构造模拟仓储,并用Received(1)验证调用。 - 仓储测试:解析自定义仓储(如
IUserRepository)并测试按邮箱查询等方法。 - 中间件模块测试:如 KeyVault 模块的
IEafKeyVaultManager.GetSecretAsync、OpenTelemetry 模块的配置检查。
覆盖率
运行测试并收集覆盖率:
dotnet test --collect:"XPlat Code Coverage"
coverlet.runsettings 配置示例:
<?xml version="1.0" encoding="utf-8" ?>
<RunSettings>
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat Code Coverage">
<Configuration>
<Format>opencover,cobertura</Format>
<Exclude>[Eaf.*Tests]*,[*.Module]*</Exclude>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
常见测试模式
- Arrange-Act-Assert 模式。
- 异步方法测试(
async Task+await)。 - 异常测试:
Should.Throw<ArgumentException>与await Should.ThrowAsync<ArgumentException>。 - 数据驱动测试:
[Theory]配合[InlineData]。
Shouldly 常用断言
相等(ShouldBe/ShouldNotBe)、空值检查(ShouldNotBeNull/ShouldBeNull)、布尔(ShouldBeTrue/ShouldBeFalse)、集合(Count.ShouldBe、ShouldContain/ShouldNotContain)、异常(Should.Throw<Exception>)。
NSubstitute 常用写法
设置返回值:_repository.GetAsync(id).Returns(user)、_repository.GetAll().Returns(users.AsQueryable())。验证调用:await _repository.Received(1).GetAsync(id)、_repository.DidNotReceive().Delete(id)。参数匹配:Arg.Any<Guid>()、Arg.Is<Guid>(x => x != Guid.Empty)。
最佳实践
- 命名采用葡萄牙语 BDD 模式
Dado_Quando_Entao,描述被测场景。 - 测试相互独立,不依赖执行顺序,每个测试后清理。
- 保持测试简单聚焦,变量命名具描述性,尽量每个测试一个断言。
- 对外部依赖使用模拟,避免慢操作,尽可能并行运行。
- 同时测试成功与失败场景,目标覆盖率大于 90%,提交前运行测试。
常见问题与解决
- 测试初始化:在构造函数中解析依赖。
- 数据库上下文:在
PreInitialize中调用UseInMemoryDatabase()。 - 权限问题:在
PostInitialize中通过UsingDbContext添加PermissionGrant。
适用场景与限制
适用于使用 EAF/ABP 框架、以 .NET 10 与 xUnit 技术栈编写模块测试的开发者。资料未提供该技能的安装命令、收费方式、平台限制或具体版本兼容性说明,这些信息暂未提供。