Search code examples
f#testcasenunit-3.0

Testcases for NUnit 3 tests in F#


I am trying to set up a test suite for an F# project using NUnit. It seems that especially when testing things like parsers and type checkers one typically has a list of valid input data and a list of invalid data. The tests itself are practically identical, so I am looking for a clever way to avoid writing a test function for each and every data item and instead seperate the test function from the data. Apparently, there seems to be something called test cases for that but I am having a hard time to find comprehensive documentation for the usage of NUnit 3 with F# in general and specifically a best practice example for my scenario.

Any pointers and hints are greately appreaciated!


Solution

  • In NUnit3 there is TestCaseSource and TestCaseData and for the best practices part I added FsUnit:

    namespace NUnit3Demo
    
    open NUnit.Framework
    open FsUnit
    
    [<TestFixture>]
    module MyTest = 
    
        let methodToBeTested s = 
            if String.length s > 3 then failwith "Something's wrong"
            else String.length s
    
        let validData =
            [
                TestCaseData("   ").Returns(3)
                TestCaseData("").Returns(0)
                TestCaseData("a").Returns(1)
            ]
    
        let invalidData =
            [
                "    "
                "abcd"
                "whatever"
            ]
    
        let otherInvalidData =
            [
                "just"
                "because"
            ]
    
        [<TestCaseSource("invalidData");
          TestCaseSource("otherInvalidData")>]
        let ``More than 3 characters throws`` s = 
            (fun () -> methodToBeTested s |> ignore)
            |> should throw typeof<System.Exception>
    
        [<TestCaseSource("validData")>]
        let ``Less than 4 characters returns length`` s = 
            methodToBeTested s
    

    Note that TestCaseData can take and return arbitrary objects (obviously they should match the test signatures). Also, the data can be written even nicer:

    let validData =
        [
            "   ", 3
            "",    0
            "a",   1
        ] |> List.map (fun (d, r) -> TestCaseData(d).Returns r)