
Fsharp Testing
Structure F# tests with xUnit, FsUnit, Unquote, FsCheck properties, mocks, Testcontainers, and WebApplicationFactory for ASP.NET Core services.
Overview
F# Testing Patterns is an agent skill most often used in Ship (also Build backend) that documents xUnit, FsUnit, Unquote, FsCheck, and integration testing practices for F# and ASP.NET Core.
Install
npx skills add https://github.com/affaan-m/everything-claude-code --skill fsharp-testingWhat is this skill?
- Stack table: xUnit, FsUnit.xUnit, Unquote quotations, FsCheck.xUnit, NSubstitute, Testcontainers, WebApplicationFactory
- FsUnit should/equal and ofCase assertion patterns for readable F# unit tests
- Property-based testing integration with FsCheck for invariant checking
- Integration tests with real infrastructure via Testcontainers
- ASP.NET Core end-to-end tests using WebApplicationFactory
- 8-tool test framework stack documented in the skill table
Adoption & trust: 1.1k installs on skills.sh; 210k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your F# service lacks consistent assertions, property tests, or integration harnesses and reviews keep flagging flaky or opaque failures.
Who is it for?
Indie builders shipping F# APIs or ASP.NET Core apps who want agent-generated tests that match community .NET conventions.
Skip if: Greenfield projects in Python or TypeScript-only stacks, or teams that standardized on Expecto instead of xUnit without migration appetite.
When should I use this skill?
Writing new tests for F# code, reviewing test quality and coverage, setting up test infrastructure for F# projects, or debugging flaky or slow tests.
What do I get? / Deliverables
You get copy-ready F# test modules using the standard .NET testing stack plus guidance for integration and property-based coverage before merge.
- F# test modules with Fact/Theory tests
- Property-based FsCheck suites
- ASP.NET Core integration test setup notes
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Canonical shelf is Ship → testing because the skill governs test quality, flakes, and integration harnesses before release. Content centers on xUnit facts, property tests, and ASP.NET Core integration patterns—not feature implementation or DevOps deploy.
Where it fits
Add FsCheck properties around order state transitions before a release candidate.
Scaffold OrderServiceTests with FsUnit should equal while implementing domain modules.
Review agent-generated tests for quotation clarity and missing integration coverage on API endpoints.
How it compares
F#-specific test cookbook for xUnit/FsCheck—not a generic red-green refactor loop and not a Playwright E2E skill.
Common Questions / FAQ
Who is fsharp-testing for?
Solo and indie developers writing F# domain logic or ASP.NET Core microservices who want FsUnit, Unquote, and FsCheck patterns in one place.
When should I use fsharp-testing?
During Ship testing when adding or reviewing tests; during Build backend when scaffolding a new F# service test project; when debugging flaky Testcontainers or WebApplicationFactory suites.
Is fsharp-testing safe to install?
It is documentation and code patterns only; Testcontainers and integration tests may need Docker and network locally. Review the Security Audits panel on this Prism page.
SKILL.md
READMESKILL.md - Fsharp Testing
# F# Testing Patterns Comprehensive testing patterns for F# applications using xUnit, FsUnit, Unquote, FsCheck, and modern .NET testing practices. ## When to Activate - Writing new tests for F# code - Reviewing test quality and coverage - Setting up test infrastructure for F# projects - Debugging flaky or slow tests ## Test Framework Stack | 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 | | **Testcontainers** | Real infrastructure in integration tests | | **WebApplicationFactory** | ASP.NET Core integration tests | ## Unit Tests with xUnit + FsUnit ### Basic Test Structure ```fsharp 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 @>) ``` ### Assertions with Unquote Unquote uses F# quotations so failure messages show the full expression that failed, not just "expected X got Y". ```fsharp 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 @> ``` ### Async Tests ```fsharp [<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 @> } ``` ### Parameterized Tests with Theory ```fsharp [<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("user@example.com", true)>] [<InlineData("user+tag@example.co.uk", true)>] let ``IsValidEmail returns expected result`` (email: string, expected: bool) = test <@ EmailValidator.isValid email = expected @> ``` ## Property-Based Testing with FsCheck ### Using FsCheck.xUnit ```fsharp 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`` (orde