Intro

In the last article, I discussed property-based testing. What I did not discuss was setting up the preconditions necessary for a function to be tested.

Test Conditions

Consider the following function:

let createGrid rowCount = 

    [for x in 0..rowCount-1 do
        for y in 0..rowCount-1 do
            yield { X=x; Y=y; State=Dead }
    ]|> List.map (fun c -> (c.X, c.Y), { X=c.X; Y=c.Y; State=Dead })
     |> Map.ofList

The above function generates a grid from the rowCount argument provided by a client. If we wanted to provide a property-based test for this function, we could write it such that the number of cells generated from the function equals the rowCount that we passed in as input, squared.

The following code is a property-based test:

[<Fact>]
let ``number of cells in grid equals rowcount squared`` () =
    Check.QuickThrowOnFailure <| fun rowCount -> rowCount |> createGrid
                                                          |> Map.toList
                                                          |> List.length = rowCount * rowCount

If we run the above test, we will observe the following failure.

System.Exception : Falsifiable, after 3 tests (1 shrink) (StdGen (199136793,296133941)):
Original:
-2
Shrunk:
-1

Setting Preconditions

For the above test to pass, we need to setup some conditions for the range of inputs. For example, this function probably shouldn’t accept negative values for the number of rows. Therefore, to specify the conditions for the valid range of inputs to our function under test, we can rewrite the test and specify the conditions for a valid range of inputs.

The following test includes conditions for valid inputs:

[<Fact>]
let ``number of cells in grid equals rowcount squared`` () =
    Check.QuickThrowOnFailure <| fun rowCount -> rowCount >= 0 ==> (rowCount |> createGrid
                                                                             |> Map.toList
                                                                             |> List.length = rowCount * rowCount)

Observe the precondition that we provided:

rowCount >= 0

We then feed that precondition into our test with the following operator:

==>

If we run the test now, the test will pass.

Conclusion

In conclusion, I discussed setting up conditions for valid inputs in regards to property-based testing.

Advertisements