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 type
s / 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#?