with one click
fsharp-testing
F# testing patterns with xUnit, FsUnit, Unquote, FsCheck property-based testing, integration tests, and test organization best practices.
F# testing patterns with xUnit, FsUnit, Unquote, FsCheck property-based testing, integration tests, and test organization best practices.
React 18/19 patterns including hooks discipline, server/client component boundaries, Suspense + error boundaries, form actions, data fetching, state management decision trees, and accessibility-first composition. Use when writing or reviewing React components.
React and Next.js performance optimization patterns adapted from Vercel Engineering's React Best Practices (https://github.com/vercel-labs/agent-skills). Organizes 70+ rules across 8 priority categories — waterfalls, bundle size, server-side, client fetching, re-render, rendering, JS micro-perf, advanced. Use when writing, reviewing, or refactoring React/Next.js code for performance.
React component testing with React Testing Library, Vitest/Jest, MSW for network mocking, accessibility assertions with axe, and the decision boundary between component tests and Playwright/Cypress end-to-end runs. Use when writing or fixing tests for React components, hooks, or pages.
Agent-driven scheduling and publishing of social media posts across 13 platforms via SocialClaw. Use when the user wants to publish to X, LinkedIn, Instagram, Facebook Pages, TikTok, Discord, Telegram, YouTube, Reddit, WordPress, or Pinterest — or when managing campaigns, uploading media, or monitoring post delivery status.
End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches.
Accessibility patterns for React and Next.js — semantic HTML, ARIA attributes, form labeling, keyboard navigation, focus management, and screen reader support. Use when building any interactive UI component or form.
| name | fsharp-testing |
| description | F# testing patterns with xUnit, FsUnit, Unquote, FsCheck property-based testing, integration tests, and test organization best practices. |
| origin | ECC |
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 |
| Testcontainers | 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("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 @>
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
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 ``valid emails pass validation`` (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 saves the confirmed order`` () = 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 ``calls repository with correct 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 order returns 404 when not found`` () = 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
| Anti-Pattern | Fix |
|---|---|
| Testing implementation details | Test behavior and outcomes |
| Mutable shared test state | Fresh state per test |
Thread.Sleep in async tests | Use Task.Delay with timeout, or polling helpers |
Asserting on sprintf output | Assert on typed values and pattern matches |
Ignoring CancellationToken | Always pass and verify cancellation |
| Skipping property-based tests | Use FsCheck for any function with clear invariants |
dotnet-patterns - Idiomatic .NET patterns, dependency injection, and architecturecsharp-testing - C# testing patterns (shared infrastructure like WebApplicationFactory and Testcontainers applies to F# too)# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"
# Run specific project
dotnet test tests/MyApp.Tests/
# Filter by test name
dotnet test --filter "FullyQualifiedName~OrderService"
# Watch mode during development
dotnet watch test --project tests/MyApp.Tests/