Testing

42.1 Expecto

open Expecto

let tests =
    testList "Math" [
        test "addition" {
            Expect.equal (1 + 1) 2 "1 + 1 should be 2"
        }
        test "list reverse twice" {
            let xs = [1; 2; 3]
            Expect.equal (List.rev (List.rev xs)) xs "reversing twice is identity"
        }
        testCase "throws on divide by zero" <| fun _ ->
            Expect.throws (fun () -> 1 / 0 |> ignore) "should throw"
        testAsync "async test" {
            let! v = async { return 42 }
            Expect.equal v 42 "async value"
        }
        testProperty "addition is commutative" <| fun (a: int) (b: int) -> a + b = b + a   // FsCheck integration
    ]

[<EntryPoint>]
let main argv = runTestsWithCLIArgs [] argv tests

42.2 xUnit / NUnit with F#

Other tools: NUnit, MSTest, TUnit, Verify (snapshot testing), NSubstitute/Foq (mocking, though F# often doesn't need mocks thanks to function injection), Bogus (fake data).

open Xunit
open FsUnit.Xunit                       // fluent assertions: x |> should equal 2

[<Fact>]
let ``adding two numbers works`` () =
    Assert.Equal(4, 2 + 2)

[<Theory>]
[<InlineData(1, 2, 3)>]
[<InlineData(0, 0, 0)>]
let ``add returns the sum`` (a: int, b: int, expected: int) =
    a + b |> should equal expected

// Unquote: assertions as quotations with step-by-step failure messages
open Swensen.Unquote
[<Fact>]
let ``unquote example`` () = test <@ List.sum [1; 2; 3] = 6 @>
dotnet test
dotnet test --collect:"XPlat Code Coverage"