- 描述整体基于ASP.NET Core的分层架构与领域驱动设计 - 详细说明表现层、视图模型层、配置层和基础设施层职责 - 介绍用户认证、OAuth2授权码与令牌颁发的数据流过程 - 抽象说明用户与租户、声明和授权实体设计 - 说明应用启动入口和关键HTTP端点 - 列出错误处理策略和跨领域关注点(日志、追踪、安全) docs(concerns): 新增代码库问题与关注点分析文档 - 汇总并详述安全漏洞如配置文件泄露、Cookie策略不当 - 记录技术债务包括缺乏单元测试、依赖注入不统一等 - 罗列性能问题和具体代码缺陷 - 给出优先级明确的修复建议和改进措施 - 涵盖架构设计问题和依赖兼容性风险 - 说明测试覆盖缺口及高风险未测试区域 docs(conventions): 新增编码约定与规范文档 - 明确文件、类、方法、变量等命名规则 - 规范代码风格包括命名空间、主构造函数使用 - 制定日志记录、审计日志和依赖注入的标准 - 规定控制器路由、异步模式和错误处理方式 - 说明DTO命名及特性使用规范 - 描述配置管理、注释规范及常用代码注释样例 docs(integrations): 添加外部系统集成文档 - 介绍数据库连接和PostgreSQL客户端库版本 - 描述身份认证与授权服务及默认用户信息 - 说明可观测性方案及OpenTelemetry组件 - 涵盖容器化部署相关Docker与Kubernetes配置 - 说明CI/CD流水线触发条件与构建流程 - 列出环境变量需求和主要API端点 - 强调生产环境密钥管理与安全存储机制
11 KiB
11 KiB
测试模式
分析日期: 2026-02-28
测试框架概述
此代码库目前未建立独立的测试项目,但具备测试基础设施支持。代码库使用以下测试相关配置:
appsettings.Testing.json- 测试环境配置文件Microsoft.EntityFrameworkCore.InMemory- 内存数据库提供程序(用于测试场景)- 支持通过配置切换测试模式
测试配置
测试环境配置
配置文件位置: src/appsettings.Testing.json
配置内容:
{
"Testing": true,
"ConnectionStrings": {
"DefaultConnection": "..."
},
"OpenIddict": {
"Issuer": "...",
"Audience": "..."
}
}
测试模式切换
约定: 通过配置项 Testing 控制是否启用完整 OpenIddict 配置
示例:
var isTesting = builder.Configuration.GetValue<bool>("Testing", false);
if (!isTesting)
{
builder.AddServer(options =>
{
// 生产环境 OpenIddict 配置
options.SetIssuer(configuration["OpenIddict:Issuer"] ?? "http://localhost:5132");
// ... 其他配置
});
}
内存数据库
依赖包: Microsoft.EntityFrameworkCore.InMemory
使用场景: 测试环境中使用内存数据库,避免外部数据库依赖
示例:
// 在测试环境中,可以使用 InMemory 提供程序
options.UseInMemoryDatabase("TestDatabase");
测试现状分析
缺失的测试项目
问题: 当前代码库没有独立的测试项目(xUnit、NUnit 或 MSTest)
影响:
- 缺少单元测试
- 缺少集成测试
- 缺少控制器测试
- 无法进行回归测试
建议的测试策略
由于代码库采用 Clean Architecture 和 MediatR 模式,建议建立以下测试层次:
1. 单元测试(xUnit/NUnit)
目标: 验证业务逻辑、命令处理器、查询处理器
测试框架建议:
- xUnit - 推荐,与 .NET 生态集成良好
- NUnit - 传统选择,功能全面
- FluentAssertions - 断言库,提供更自然的断言语法
- Moq - 模拟框架
示例测试结构:
public class UsersControllerTests
{
private readonly Mock<UserManager<ApplicationUser>> _userManagerMock;
private readonly Mock<RoleManager<ApplicationRole>> _roleManagerMock;
private readonly Mock<ILogger<UsersController>> _loggerMock;
private readonly PlatformDbContext _context;
private readonly UsersController _controller;
public UsersControllerTests()
{
// 设置模拟对象
var store = new Mock<IUserStore<ApplicationUser>>();
_userManagerMock = new Mock<UserManager<ApplicationUser>>(
store.Object, null, null, null, null, null, null, null, null);
_roleManagerMock = new Mock<RoleManager<ApplicationRole>>();
_loggerMock = new Mock<ILogger<UsersController>>();
// 使用内存数据库
var options = new DbContextOptionsBuilder<PlatformDbContext>()
.UseInMemoryDatabase(databaseName: "TestDb")
.Options;
_context = new PlatformDbContext(options);
_controller = new UsersController(
_userManagerMock.Object,
_roleManagerMock.Object,
_loggerMock.Object,
_context);
}
}
2. 集成测试
目标: 测试控制器端点、数据库操作、OpenIddict 流程
测试框架建议:
- Microsoft.AspNetCore.Mvc.Testing - ASP.NET Core 测试支持
- TestHost - 内存 Web 主机
- FluentAssertions - 断言库
示例测试结构:
public class UsersControllerIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public UsersControllerIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
[Fact]
public async Task GetUsers_ReturnsPagedResults()
{
// Arrange
var request = "/api/users?page=1&pageSize=10";
// Act
var response = await _client.GetAsync(request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadAsStringAsync();
// 验证响应内容
}
}
3. 端到端测试
目标: 验证完整用户流程(可选)
工具建议:
- Playwright - 现代 Web 测试框架
- Selenium - 传统选择
测试文件组织
建议的目录结构
fengling-auth-service/
├── src/
│ └── Fengling.AuthService.csproj
└── tests/
├── Fengling.AuthService.UnitTests/
│ ├── Fengling.AuthService.UnitTests.csproj
│ ├── Controllers/
│ ├── Commands/
│ ├── Queries/
│ └── Services/
├── Fengling.AuthService.IntegrationTests/
│ ├── Fengling.AuthService.IntegrationTests.csproj
│ ├── Controllers/
│ └── Fixtures/
└── Fengling.AuthService.E2ETests/
├── Fengling.AuthService.E2ETests.csproj
└── Scenarios/
测试项目依赖
建议的测试项目 csproj 配置:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="*" />
<PackageReference Include="FluentAssertions" Version="*" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="*" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="*" />
<PackageReference Include="Moq" Version="*" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Fengling.AuthService.csproj" />
</ItemGroup>
</Project>
模拟模式
Moq 使用示例
控制器依赖模拟:
// 模拟 UserManager
var userStoreMock = new Mock<IUserStore<ApplicationUser>>();
var userManagerMock = new Mock<UserManager<ApplicationUser>>(
userStoreMock.Object,
null, null, null, null, null, null, null, null);
// 模拟 RoleManager
var roleStoreMock = new Mock<IRoleStore<ApplicationRole>>();
var roleManagerMock = new Mock<RoleManager<ApplicationRole>>(
roleStoreMock.Object,
null, null, null, null, null, null, null);
// 模拟 ILogger
var loggerMock = new Mock<ILogger<UsersController>>();
// 设置模拟行为
userManagerMock
.Setup(x => x.FindByNameAsync(It.IsAny<string>()))
.ReturnsAsync(new ApplicationUser { UserName = "testuser" });
userManagerMock
.Setup(x => x.GetRolesAsync(It.IsAny<ApplicationUser>()))
.ReturnsAsync(new List<string> { "User" });
数据库模拟
使用 InMemory 数据库:
var options = new DbContextOptionsBuilder<PlatformDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var context = new PlatformDbContext(options);
// 添加测试数据
context.Users.Add(new ApplicationUser
{
UserName = "testuser",
Email = "test@example.com"
});
await context.SaveChangesAsync();
测试数据工厂
建议的 Fixture 模式
public static class UserFixture
{
public static ApplicationUser CreateUser(string userName = "testuser")
{
return new ApplicationUser
{
UserName = userName,
Email = $"{userName}@example.com",
TenantInfo = new TenantInfo(new Tenant { TenantId = 1, TenantCode = "TEST" }),
EmailConfirmed = true,
CreatedTime = DateTimeOffset.UtcNow
};
}
public static ApplicationUser CreateAdminUser()
{
var user = CreateUser("admin");
return user;
}
}
public static class RoleFixture
{
public static ApplicationRole CreateRole(string name = "User")
{
return new ApplicationRole
{
Name = name,
DisplayName = name,
Description = $"{name} Role",
IsSystem = false,
CreatedTime = DateTime.UtcNow
};
}
public static ApplicationRole CreateSystemRole()
{
var role = CreateRole("Admin");
role.IsSystem = true;
return role;
}
}
断言模式
FluentAssertions 使用示例
using FluentAssertions;
// 对象断言
result.Should().NotBeNull();
result.Should().BeOfType<ApplicationUser>();
result.Id.Should().Be(1);
result.UserName.Should().Be("testuser");
// 集合断言
users.Should().HaveCount(10);
users.Should().Contain(u => u.UserName == "testuser");
users.Should().BeInAscendingOrder(u => u.CreatedTime);
// 异常断言
action.Should().Throw<InvalidOperationException>()
.WithMessage("User not found");
// HTTP 响应断言
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType.MediaType.Should().Be("application/json");
测试覆盖率
覆盖率目标建议
| 测试类型 | 覆盖率目标 | 说明 |
|---|---|---|
| 单元测试 | ≥ 80% | 业务逻辑、命令、查询 |
| 集成测试 | ≥ 60% | 控制器端点、数据访问 |
| 整体 | ≥ 70% | 综合覆盖率 |
覆盖率工具
工具: coverlet.collector 或 coverlet.msbuild
运行命令:
# 使用 dotnet test
dotnet test --collect:"XPlat Code Coverage"
# 查看覆盖率报告
dotnet test --collect:"XPlat Code Coverage" --results-directory ./coverage
CI/CD 测试集成
建议的测试脚本
# azure-pipelines.yml 或 .github/workflows/test.yml
trigger:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '10.0.x'
- script: |
dotnet restore
dotnet build --configuration Release
displayName: 'Build'
- script: |
dotnet test --configuration Release --collect:"XPlat Code Coverage" --results-directory ./coverage
displayName: 'Test'
- script: |
dotnet tool install --global dotnet-reportgenerator-globaltool
reportgenerator -reports:./coverage/coverage.cobertura.xml -targetdir:./coverage-report -reporttypes:Html
displayName: 'Generate Coverage Report'
当前测试缺口
未测试的关键领域
- 控制器端点 - 缺少 API 端点测试
- 业务逻辑 - 命令处理器和查询处理器未测试
- 身份验证流程 - OAuth2/OIDC 流程未测试
- 多租户隔离 - 租户数据隔离逻辑未验证
- 角色权限 - RBAC 逻辑未测试
优先测试建议
基于代码库特点,建议按以下优先级添加测试:
-
高优先级
TokenController- 令牌颁发逻辑UsersController- 用户 CRUD 操作RolesController- 角色管理操作
-
中优先级
AuthorizationController- 授权流程- 审计日志功能
- 多租户数据隔离
-
低优先级
- 健康检查端点
- 静态文件服务
测试模式分析:2026-02-28