用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/aaione/everything-claude-code-zh --skill fsharp-testing命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Kubernetes 工作负载模式、资源管理、RBAC、probes、autoscaling、ConfigMap/Secret 处理,以及面向生产级部署的 kubectl 调试。
完成任何非平凡任务后使用。智能体按 5 个维度自评输出——准确性、完整性、清晰度、可执行性、简洁性——每项都给出具体证据。生成结构化 1-5 评分卡和具体改进建议。
在 competitive-platform-analysis 产出分层竞品集合后使用。按九个加权维度(定位、声音、视觉工艺、offer packaging、证据、enterprise-readiness、thought leadership、定价、客户 strategic tension)为每个竞品评分,使用明确 1–5 rubrics 和 tension-plot。位于 competitive-report-structure 之前。
基于 SOC 职业分类
正在显示 SKILL.md
| name | fsharp-testing |
| description | F# 测试模式——xUnit、FsUnit、Unquote、FsCheck 基于属性的测试、集成测试和测试组织最佳实践。 |
| origin | ECC |
使用 xUnit、FsUnit、Unquote、FsCheck 和现代 .NET 测试实践的全面 F# 应用测试模式。
| 工具 | 用途 |
|---|---|
| xUnit | 测试框架(标准 .NET 生态选择) |
| FsUnit.xUnit | F# 友好的 xUnit 断言语法 |
| Unquote | 使用 F# 引用的断言库,提供清晰的失败消息 |
| FsCheck.xUnit | 与 xUnit 集成的基于属性的测试 |
| NSubstitute | .NET 依赖 Mock |
| Testcontainers | 集成测试中的真实基础设施 |
| WebApplicationFactory | ASP.NET Core 集成测试 |
module OrderServiceTests
open Xunit
open FsUnit.Xunit
[<Fact>]
let ``create 将状态设置为 Pending`` () =
let order = Order.create "cust-1" [ validItem ]
order.Status |> should equal Pending
[<Fact>]
let ``confirm 将状态更改为 Confirmed`` () =
let order = Order.create "cust-1" [ validItem ]
let confirmed = Order.confirm order
confirmed.Status |> should be (ofCase <@ Confirmed @>)
Unquote 使用 F# 引用,使失败消息显示失败的完整表达式,而非仅仅"期望 X 得到 Y"。
module OrderValidationTests
open Xunit
open Swensen.Unquote
[<Fact>]
let ``PlaceOrder 在请求有效时返回成功`` () =
let request = { CustomerId = "cust-123"; Items = [ validItem ] }
let result = OrderService.placeOrder request
test <@ Result.isOk result @>
[<Fact>]
let ``订单总价汇总项目价格`` () =
let items = [ { Sku = "A"; Quantity = 2; Price = 10m }
{ Sku = "B"; Quantity = 1; Price = 5m } ]
let total = Order.calculateTotal items
test <@ total = 25m @>
[<Fact>]
let ``验证后的邮箱拒绝空输入`` () =
let result = ValidatedEmail.create ""
test <@ Result.isError result @>
[<Fact>]
let ``PlaceOrder 在请求有效时返回成功`` () = task {
let deps = createTestDeps ()
let request = { CustomerId = "cust-123"; Items = [ validItem ] }
let! result = OrderService.placeOrder deps request
test <@ Result.isOk result @>
}
[<Fact>]
let ``PlaceOrder 在项目为空时返回错误`` () = 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 拒绝空的客户 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("user@example.com", true)>]
[<InlineData("user+tag@example.co.uk", true)>]
let ``IsValidEmail 返回预期结果`` (email: string, expected: bool) =
test <@ EmailValidator.isValid email = expected @>
open FsCheck
open FsCheck.Xunit
[<Property>]
let ``订单总价始终非负`` (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 ``序列化往返`` (order: Order) =
let json = JsonSerializer.Serialize order
let deserialized = JsonSerializer.Deserialize<Order> json
deserialized = order
type OrderGenerators =
static member ValidEmail () =
gen {
let! user = Gen.elements [ "alice"; "bob"; "carol" ]
let! domain = Gen.elements [ "example.com"; "test.org" ]
return $"{user}@{domain}"
}
|> Arb.fromGen
[<Property(Arbitrary = [| typeof<OrderGenerators> |])>]
let ``有效邮箱通过验证`` (email: string) =
EmailValidator.isValid email
let createTestDeps () =
let mutable savedOrders = []
{ FindOrder = fun id -> task { return Map.tryFind id testData }
SaveOrder = fun order -> task { savedOrders <- order :: savedOrders }
SendNotification = fun _ -> Task.CompletedTask }
[<Fact>]
let ``PlaceOrder 保存已确认的订单`` () = task {
let mutable saved = []
let deps =
{ createTestDeps () with
SaveOrder = fun order -> task { saved <- order :: saved } }
let! _ = OrderService.placeOrder deps validRequest
test <@ saved.Length = 1 @>
}
open NSubstitute
[<Fact>]
let ``使用正确的 ID 调用仓库`` () = task {
let repo = Substitute.For<IOrderRepository>()
repo.FindByIdAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>())
.Returns(Task.FromResult(Some testOrder))
let service = OrderService(repo)
let! _ = service.GetOrder(testOrder.Id, CancellationToken.None)
do! repo.Received(1).FindByIdAsync(testOrder.Id, Arg.Any<CancellationToken>())
}
type OrderApiTests (factory: WebApplicationFactory<Program>) =
interface IClassFixture<WebApplicationFactory<Program>>
let client =
factory.WithWebHostBuilder(fun builder ->
builder.ConfigureServices(fun services ->
services.RemoveAll<DbContextOptions<AppDbContext>>() |> ignore
services.AddDbContext<AppDbContext>(fun options ->
options.UseInMemoryDatabase("TestDb") |> ignore) |> ignore))
.CreateClient()
[<Fact>]
member _.``GET 订单未找到时返回 404`` () = task {
let! response = client.GetAsync($"/api/orders/{Guid.NewGuid()}")
test <@ response.StatusCode = HttpStatusCode.NotFound @>
}
tests/
MyApp.Tests/
Unit/
OrderServiceTests.fs
PaymentServiceTests.fs
Integration/
OrderApiTests.fs
OrderRepositoryTests.fs
Properties/
OrderPropertyTests.fs
Helpers/
TestData.fs
TestDeps.fs
| 反模式 | 修复 |
|---|---|
| 测试实现细节 | 测试行为和结果 |
| 可变的共享测试状态 | 每个测试使用新状态 |
异步测试中的 Thread.Sleep | 使用带超时的 Task.Delay 或轮询辅助 |
对 sprintf 输出断言 | 对类型化值和模式匹配断言 |
忽略 CancellationToken | 始终传递并验证取消 |
| 跳过基于属性的测试 | 对有清晰不变量的函数使用 FsCheck |
dotnet-patterns - 惯用的 .NET 模式、依赖注入和架构csharp-testing - C# 测试模式(共享的基础设施如 WebApplicationFactory 和 Testcontainers 也适用于 F#)# 运行所有测试
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/