AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Fsharp Testing

skill-affaan-m-ecc-fsharp-testing · by affaan-m

F# testing patterns with xUnit, FsUnit, Unquote, FsCheck property-based testing, integration tests, and test organization best practices.

No reviews yet
0 installs
44 views
0.0% view→install

Install

$ agentstack add skill-affaan-m-ecc-fsharp-testing

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-affaan-m-ecc-fsharp-testing)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Fsharp Testing? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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

module OrderServiceTests

open Xunit
open FsUnit.Xunit

[]
let ``create sets status to Pending`` () =
    let order = Order.create "cust-1" [ validItem ]
    order.Status |> should equal Pending

[]
let ``confirm changes status to Confirmed`` () =
    let order = Order.create "cust-1" [ validItem ]
    let confirmed = Order.confirm order
    confirmed.Status |> should be (ofCase )

Assertions with Unquote

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

[]
let ``PlaceOrder returns success when request is valid`` () =
    let request = { CustomerId = "cust-123"; Items = [ validItem ] }
    let result = OrderService.placeOrder request
    test 

[]
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 

[]
let ``validated email rejects empty input`` () =
    let result = ValidatedEmail.create ""
    test 

Async Tests

[]
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 
}

[]
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 
}

Parameterized Tests with Theory

[]
[]
[]
let ``PlaceOrder rejects empty customer ID`` (customerId: string) =
    let request = { CustomerId = customerId; Items = [ validItem ] }
    let result = OrderService.placeOrder request
    result |> should be (ofCase )

[]
[]
[]
[]
[]
let ``IsValidEmail returns expected result`` (email: string, expected: bool) =
    test 

Property-Based Testing with FsCheck

Using FsCheck.xUnit

open FsCheck
open FsCheck.Xunit

[]
let ``order total is always non-negative`` (items: NonEmptyList) =
    let orderItems =
        items.Get
        |> List.map (fun (qty, price) ->
            { Sku = "SKU"; Quantity = qty.Get; Price = abs price })
    let total = Order.calculateTotal orderItems
    total >= 0m

[]
let ``serialization roundtrips`` (order: Order) =
    let json = JsonSerializer.Serialize order
    let deserialized = JsonSerializer.Deserialize json
    deserialized = order

Custom Generators

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

[ |])>]
let ``valid emails pass validation`` (email: string) =
    EmailValidator.isValid email

Mocking Dependencies

Function Stubs (Preferred)

let createTestDeps () =
    let mutable savedOrders = []
    { FindOrder = fun id -> task { return Map.tryFind id testData }
      SaveOrder = fun order -> task { savedOrders  Task.CompletedTask }

[]
let ``PlaceOrder saves the confirmed order`` () = task {
    let mutable saved = []
    let deps =
        { createTestDeps () with
            SaveOrder = fun order -> task { saved 
}

NSubstitute for .NET Interfaces

open NSubstitute

[]
let ``calls repository with correct ID`` () = task {
    let repo = Substitute.For()
    repo.FindByIdAsync(Arg.Any(), Arg.Any())
        .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())
}

ASP.NET Core Integration Tests

type OrderApiTests (factory: WebApplicationFactory) =
    interface IClassFixture>

    let client =
        factory.WithWebHostBuilder(fun builder ->
            builder.ConfigureServices(fun services ->
                services.RemoveAll>() |> ignore
                services.AddDbContext(fun options ->
                    options.UseInMemoryDatabase("TestDb") |> ignore) |> ignore))
            .CreateClient()

    []
    member _.``GET order returns 404 when not found`` () = task {
        let! response = client.GetAsync($"/api/orders/{Guid.NewGuid()}")
        test 
    }

Test Organization

tests/
  MyApp.Tests/
    Unit/
      OrderServiceTests.fs
      PaymentServiceTests.fs
    Integration/
      OrderApiTests.fs
      OrderRepositoryTests.fs
    Properties/
      OrderPropertyTests.fs
    Helpers/
      TestData.fs
      TestDeps.fs

Common Anti-Patterns

| 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 |

Related Skills

  • dotnet-patterns - Idiomatic .NET patterns, dependency injection, and architecture
  • csharp-testing - C# testing patterns (shared infrastructure like WebApplicationFactory and Testcontainers applies to F# too)

Running Tests

# 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/

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.