2
\$\begingroup\$

I am writing some unit tests using XUnit in F# and I am wondering what is the most idiomatic way in the F# sense to write them.

Let's start a simple case:

BicValidationTests.fs:

module Rm.Bai.Domain.BicValidationTests
open Rm.Bai.Domain.BicValidation
open FsUnit
open Xunit

let shortValidBic    = "MYMB gb 2L"
let expectedShortBic = "MYMBGB2L"
let ok               = Result<string, ValidationError>.Ok
let error            = Result<string, ValidationError>.Error

[<Fact>]
let ``Short Bic should be valid when all rules are respected`` () =
    shortValidBic
    |> validateAndFormat
    |> should equal (ok expectedShortBic)

One thing I am not too sure is whether it is better to include directly the values knowing that there is nothing shared among different test functions right into the test functions, such as:

[<Fact>]
let ``Short Bic should be valid when all rules are respected`` () =
    "MYMB gb 2L"
    |> validateAndFormat
    |> should equal (ok "MYMBGB2L")

This seems to be even more so the case since the answer I got here which seems to indicate that if you have several test files, you need to rely on declaring types / classes to contain let variables for things other than primitive types like number or strings.

For example:

let sepaCompliantBics = [
    "DSXLAD46";
    "KAOLADZOAQC";
    "GFDIATGN";
    "XHOCATENR1X";
    "IMITBEW1";
    "JYPPBEPW807";
    "AHODBGG2";
    "LTQJBGMRKKA";
    "LAHYHRDK";
    ...
]

[<Fact>]
let ``Bics should be valid with SEPA-compliant countries`` () =
    sepaCompliantBics
    |> List.map(fun bic -> (validateAndFormat bic, bic))
    |> List.iter(fun (validation, bic) -> validation |> should equal (ok bic))

sepaCompliantBics in the example above will be set to null if it happens to not be the first file in the test project.

The solutions are then:

type Tests() = 
    let ok               = Result<string, ValidationError>.Ok
    let error            = Result<string, ValidationError>.Error

    let sepaCompliantBics = [
        "DSXLAD46";
        "KAOLADZOAQC";
        "GFDIATGN";
        "XHOCATENR1X";
        "IMITBEW1";
        "JYPPBEPW807";
        "AHODBGG2";
        "LTQJBGMRKKA";
        "LAHYHRDK";
        ...
    ]

    [<Fact>]
    let ``Bics should be valid with SEPA-compliant countries`` () =
        sepaCompliantBics
        |> List.map(fun bic -> (validateAndFormat bic, bic))
        |> List.iter(fun (validation, bic) -> validation |> should equal (ok bic))

or:

let getSepaCompliantBics() = [
        "DSXLAD46";
        "KAOLADZOAQC";
        "GFDIATGN";
        "XHOCATENR1X";
        "IMITBEW1";
        "JYPPBEPW807";
        "AHODBGG2";
        "LTQJBGMRKKA";
        "LAHYHRDK";
        ...
    ]


[<Fact>]
let ``Bics should be valid with SEPA-compliant countries`` () =
    getSepaCompliantBics()
    |> List.map(fun bic -> (validateAndFormat bic, bic))
    |> List.iter(fun (validation, bic) -> validation |> should equal (ok bic))

or again:

[<Fact>]
let ``Bics should be valid with SEPA-compliant countries`` () =
    [
        "DSXLAD46";
        "KAOLADZOAQC";
        "GFDIATGN";
        "XHOCATENR1X";
        "IMITBEW1";
        "JYPPBEPW807";
        "AHODBGG2";
        "LTQJBGMRKKA";
        "LAHYHRDK";
        ...
        // this can be a pretty long list btw
    ]
    |> List.map(fun bic -> (validateAndFormat bic, bic))
    |> List.iter(fun (validation, bic) -> validation |> should equal (ok bic))

So I am wondering if having a long list like the example above is problem if it is defined in the test function itself rather than said outside? Is it still an idiomatic way to write unit tests with F#?

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

I think that regardless of a language you're using the most idiomatic way would be to take advantage of InlineData attribute

It would look roughly like this

[<Theory>]
[<InlineData("DSXLAD46", "expectedbic1")>]
[<InlineData("KAOLADZOAQC", "expectedbic2")>]
let ``Bics should be valid with SEPA-compliant countries`` input output =
    let result = validateAndFormat input
     match result with
        | Ok s ->
            Assert.Equal(s, output)            
        | Error _ -> Assert.True(false) //looks like there is no Assert.Fail() in xunit
\$\endgroup\$
1
  • \$\begingroup\$ I mark you answer as "the one" but will add more details in my post as an edit. \$\endgroup\$ Aug 21, 2019 at 13:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.