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

asked Aug 13, 2019 at 15:07
\$\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
answered Aug 14, 2019 at 8:38
\$\endgroup\$
1
  • \$\begingroup\$ I mark you answer as "the one" but will add more details in my post as an edit. \$\endgroup\$ Commented Aug 21, 2019 at 13:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.