用途
本技能提供 F# 应用的全面测试模式,基于 xUnit、FsUnit、Unquote、FsCheck 以及现代 .NET 测试实践。适用于以下场景:为 F# 代码编写新测试;审查测试质量与覆盖率;为 F# 项目搭建测试基础设施;调试不稳定或缓慢的测试。
测试框架栈
- xUnit:测试框架(.NET 生态标准选择)
- FsUnit.xUnit:面向 xUnit 的 F# 友好断言语法
- Unquote:使用 F# 引用的断言库,提供清晰的失败信息
- FsCheck.xUnit:与 xUnit 集成的属性测试
- NSubstitute:模拟 .NET 依赖
- Testcontainers:集成测试中的真实基础设施
- WebApplicationFactory:ASP.NET Core 集成测试
主要能力
使用 xUnit + FsUnit 的单元测试
基本测试结构使用 [<Fact>] 特性,配合 FsUnit 的 should equal、should be (ofCase ...) 等断言。
使用 Unquote 的断言
Unquote 使用 F# 引用,失败信息会展示完整失败表达式,而不仅是“期望 X 得到 Y”。使用 test <@ ... @> 进行断言。
异步测试
使用 task { ... } 计算表达式编写异步测试,配合 let! 等待异步操作。
参数化测试
使用 [<Theory>] 与 [<InlineData(...)>] 提供多组输入数据。
使用 FsCheck 的属性测试
通过 [<Property>] 特性定义属性,可使用 NonEmptyList、PositiveInt 等生成器,并支持自定义生成器(通过 Arb.fromGen 与 Arbitrary 参数)。
模拟依赖
推荐使用函数桩(function stubs)方式,通过记录类型组合可替换函数;对于 .NET 接口可使用 NSubstitute。
ASP.NET Core 集成测试
使用 WebApplicationFactory<Program> 与 IClassFixture,可替换数据库为内存数据库进行测试。
使用方式
测试组织建议目录结构:
tests/
MyApp.Tests/
Unit/
OrderServiceTests.fs
PaymentServiceTests.fs
Integration/
OrderApiTests.fs
OrderRepositoryTests.fs
Properties/
OrderPropertyTests.fs
Helpers/
TestData.fs
TestDeps.fs
运行测试命令:
# 运行所有测试
dotnet test
# 带覆盖率运行
dotnet test --collect:"XPlat Code Coverage"
# 运行特定项目
dotnet test tests/MyApp.Tests/
# 按测试名过滤
dotnet test --filter "FullyQualifiedName~OrderService"
# 开发期间监视模式
dotnet watch test --project tests/MyApp.Tests/
适用场景
- F# 项目的单元测试、属性测试与集成测试编写
- 测试基础设施搭建与测试组织
- 测试质量与覆盖率审查
- 不稳定或缓慢测试的调试
限制与注意事项
资料中列出了常见反模式及修复建议:测试实现细节应改为测试行为与结果;可变共享测试状态应改为每个测试使用全新状态;异步测试中避免 Thread.Sleep,应使用带超时的 Task.Delay 或轮询辅助;避免对 sprintf 输出断言,应断言类型化值与模式匹配;不要忽略 CancellationToken,应始终传递并验证取消;不要跳过属性测试,对任何具有明确不变量的函数使用 FsCheck。
相关技能包括 dotnet-patterns(惯用 .NET 模式、依赖注入与架构)和 csharp-testing(C# 测试模式,其中 WebApplicationFactory 与 Testcontainers 等共享基础设施同样适用于 F#)。