帮助你设计并实现 F# 单元、属性与集成测试的最佳实践
复制安装指令,让 AI 自动完成配置 · 推荐新手
请帮我安装 askskill 上的 "fsharp-testing" 技能: 1. 下载 https://raw.githubusercontent.com/affaan-m/ECC/main/skills/fsharp-testing/SKILL.md 2. 保存为 ~/.claude/skills/fsharp-testing/SKILL.md 3. 装好后重载技能,告诉我可以用了
请为这个 F# 模块生成基于 xUnit、FsUnit 和 Unquote 的单元测试,覆盖正常输入、边界情况和异常分支,并说明每类测试的目的。
一组结构清晰的 F# 单元测试代码,并附带测试覆盖思路说明。
针对这个 F# 函数设计 FsCheck 属性测试,找出适合验证的不变量、输入生成策略,以及可能暴露缺陷的反例场景。
可运行的属性测试示例,以及每个属性为何能提高正确性的解释。
请为一个中型 F# 项目设计测试组织方案,包括单元测试、集成测试、测试命名规范、目录结构,以及如何隔离外部依赖。
一份测试架构建议,涵盖项目结构、实践规范和集成测试实施方式。
Comprehensive testing patterns for F# applications using xUnit, FsUnit, Unquote, FsCheck, and modern .NET testing practices.
| Tool | Purpose |
|---|---|
| xUnit | Test framework (standard .NET ecosystem choice) |
| FsUnit.xUnit | F#-friendly assertion syntax for xUnit |
| Unquote | Assertion library using F# quotations for clear failure messages |
| FsCheck.xUnit | Property-based testing integrated with xUnit |
| NSubstitute | Mocking .NET dependencies |
| Real infrastructure in integration tests |
| WebApplicationFactory | ASP.NET Core integration tests |
module OrderServiceTests
open Xunit
open FsUnit.Xunit
[<Fact>]
let ``create sets status to Pending`` () =
let order = Order.create "cust-1" [ validItem ]
order.Status |> should equal Pending
[<Fact>]
let ``confirm changes status to Confirmed`` () =
let order = Order.create "cust-1" [ validItem ]
let confirmed = Order.confirm order
confirmed.Status |> should be (ofCase <@ Confirmed @>)
Unquote uses F# quotations so failure messages show the full expression that failed, not just "expected X got Y".
module OrderValidationTests
open Xunit
open Swensen.Unquote
[<Fact>]
let ``PlaceOrder returns success when request is valid`` () =
let request = { CustomerId = "cust-123"; Items = [ validItem ] }
let result = OrderService.placeOrder request
test <@ Result.isOk result @>
[<Fact>]
let ``order total sums item prices`` () =
let items = [ { Sku = "A"; Quantity = 2; Price = 10m }
{ Sku = "B"; Quantity = 1; Price = 5m } ]
let total = Order.calculateTotal items
test <@ total = 25m @>
[<Fact>]
let ``validated email rejects empty input`` () =
let result = ValidatedEmail.create ""
test <@ Result.isError result @>
[<Fact>]
let ``PlaceOrder returns success when request is valid`` () = task {
let deps = createTestDeps ()
let request = { CustomerId = "cust-123"; Items = [ validItem ] }
let! result = OrderService.placeOrder deps request
test <@ Result.isOk result @>
}
[<Fact>]
let ``PlaceOrder returns error when items are empty`` () = task {
let deps = createTestDeps ()
let request = { CustomerId = "cust-123"; Items = [] }
let! result = OrderService.placeOrder deps request
test <@ Result.isError result @>
}
[<Theory>]
[<InlineData("")>]
[<InlineData(" ")>]
let ``PlaceOrder rejects empty customer ID`` (customerId: string) =
let request = { CustomerId = customerId; Items = [ validItem ] }
let result = OrderService.placeOrder request
result |> should be (ofCase <@ Error @>)
[<Theory>]
[<InlineData("", false)>]
[<InlineData("a", false)>]
[<InlineData("[email protected]", true)>]
[<InlineData("[email protected]", true)>]
let ``IsValidEmail returns expected result`` (email: string, expected: bool) =
test <@ EmailValidator.isValid email = expected @>
open FsCheck
open FsCheck.Xunit
[<Property>]
let ``order total is always non-negative`` (items: NonEmptyList<PositiveInt * decimal>) =
let orderItems =
items.Get
|> List.map (fun (qty, price) ->
{ Sku = "SKU"; Quantity = qty.Get; Price = abs price })
let total = Order.calculateTotal orderItems
total >= 0m
[<Property>]
let ``serialization roundtrips`` (order: Order) =
let json = JsonSerializer.Serialize order
let deserialized = JsonSerializer.Deserialize<Order> json
deserialized = order
…
为 Quarkus 项目执行发布前验证闭环,涵盖构建、测试、扫描与差异审查。
帮助你用地道 Go 实践编写测试、基准、模糊测试并提升覆盖率。