# 测试模式 **分析日期:** 2026-02-28 ## 测试框架 **状态:** 此仓库中目前不存在测试项目。 ### 预期框架(未实现) 基于项目结构和依赖,测试项目应使用: - **测试运行器:** xUnit(.NET 标准) - **模拟:** Moq 或 NSubstitute - **内存数据库:** `Microsoft.EntityFrameworkCore.InMemory` 用于 DbContext 测试 - **断言:** FluentAssertions(可选,用于可读断言) ### 建议的项目结构 ``` Fengling.Platform.Tests/ ├── Fengling.Platform.Tests.csproj ├── Unit/ │ ├── TenantManagerTests.cs │ ├── TenantStoreTests.cs │ └── PlatformDbContextTests.cs ├── Integration/ │ └── TenantRepositoryTests.cs └── Usings.cs ``` ## 测试文件组织 ### 位置 - **模式:** 单独测试项目(`Fengling.Platform.Tests/`) - **结构:** 镜像源项目结构 ### 命名 - **测试类:** `{类名}Tests` 或 `{类名}Tests` - **测试方法:** `{方法名}_{场景}_{预期结果}` ## 测试结构 ### 套件组织(预期模式) ```csharp public class TenantManagerTests { private readonly ITenantManager _tenantManager; private readonly Mock _storeMock; public TenantManagerTests() { _storeMock = new Mock(); _tenantManager = new TenantManager(_storeMock.Object); } [Fact] public async Task FindByIdAsync_WithValidId_ReturnsTenant() { // Arrange var tenantId = 1L; var expectedTenant = new Tenant { Id = tenantId, Name = "Test" }; _storeMock.Setup(s => s.FindByIdAsync(tenantId, It.IsAny())) .ReturnsAsync(expectedTenant); // Act var result = await _tenantManager.FindByIdAsync(tenantId); // Assert Assert.NotNull(result); Assert.Equal(tenantId, result.Id); } } ``` ## 模拟 ### 框架: Moq **要模拟:** - `ITenantStore` - `TenantManager` 的依赖 - `PlatformDbContext` - 用于仓储测试 ### 模式 ```csharp // 带参数的模拟设置 _storeMock.Setup(s => s.FindByIdAsync(tenantId, It.IsAny())) .ReturnsAsync(expectedTenant); // 空返回模拟 _storeMock.Setup(s => s.FindByIdAsync(It.IsAny(), It.IsAny())) .ReturnsAsync((Tenant?)null); // 验证调用 _storeMock.Verify(s => s.CreateAsync(It.IsAny(), It.IsAny()), Times.Once); ``` **不要模拟:** - `Tenant` 实体(使用真实实例) - 值类型和简单 DTO ## 测试夹具和工厂 ### 测试数据 ```csharp public static class TenantFixture { public static Tenant CreateValidTenant(long id = 1) => new Tenant { Id = id, TenantCode = "TEST", Name = "Test Tenant", ContactName = "John Doe", ContactEmail = "john@test.com", Status = TenantStatus.Active, CreatedAt = DateTime.UtcNow }; public static IEnumerable CreateTenantCollection(int count) => Enumerable.Range(1, count) .Select(i => CreateValidTenant(i)); } ``` ### 位置 - `Tests/Fixtures/` 或 `Tests/Factories/` ## 测试类型 ### 单元测试 - **范围:** 隔离的单个类 - 目标: `TenantManager` - CRUD 操作 - 目标: `TenantStore` - 数据访问方法 - 目标: 实体验证逻辑 - **方法:** 模拟依赖,隔离测试 ### 集成测试 - **范围:** 使用 `PlatformDbContext` 的数据库操作 - **方法:** - 使用 `InMemoryDatabase` 隔离 - 使用 `DbContextOptionsBuilder` + `UseInMemoryDatabase` ```csharp public class PlatformDbContextTests { private readonly PlatformDbContext _context; public PlatformDbContextTests() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _context = new PlatformDbContext(options); } } ``` ### 端到端测试 - **不适用:** 这是一个库/项目,不是应用程序 ## 常见模式 ### 异步测试 ```csharp [Fact] public async Task GetAllAsync_ReturnsAllTenants() { // Arrange var tenants = new List { CreateValidTenant(), CreateValidTenant(2) }; _storeMock.Setup(s => s.GetAllAsync(It.IsAny())) .ReturnsAsync(tenants); // Act var result = await _tenantManager.GetAllAsync(); // Assert Assert.Equal(2, result.Count); } ``` ### 错误测试 ```csharp [Fact] public async Task CreateAsync_WithDuplicateTenantCode_ReturnsFailure() { // Arrange var tenant = CreateValidTenant(); _storeMock.Setup(s => s.FindByTenantCodeAsync(tenant.TenantCode, It.IsAny())) .ReturnsAsync(tenant); // 模拟已存在的租户 // Act var result = await _tenantManager.CreateAsync(tenant); // Assert Assert.False(result.Succeeded); } ``` ### 空参数测试 ```csharp [Fact] public async Task FindByIdAsync_WithNullId_ReturnsNull() { // Act var result = await _tenantManager.FindByIdAsync(null); // Assert Assert.Null(result); } ``` ## 覆盖率 ### 要求 - **未强制** - 目前未定义覆盖率目标 ### 建议目标 - **最低:** 领域逻辑 70% - **目标:** 关键路径(租户 CRUD)80% ### 查看覆盖率 ```bash dotnet test --collect:"XPlat Code Coverage" # 或使用 coverlet dotnet test /p:CollectCoverage=true /p:Threshold=80 ``` ## 运行测试 ### 命令(预期) ```bash dotnet test # 运行所有测试 dotnet test --filter "FullyQualifiedName~TenantManagerTests" # 运行特定类 dotnet test --verbosity normal # 详细输出 dotnet test --collect:"XPlat Code Coverage" # 带覆盖率 ``` --- *测试分析: 2026-02-28*