assert4hs
This library aims to provide a set of combinators to assert arbitrary nested data structures.
The inspiration of this library is AssertJ for Java, the composition of assertions was inspired by lens
library.
New assertions can be easily written and composed with other assertions. All failed assertions are gathered and presented to the user.
data Foo = Foo {name :: String, age :: Int} deriving (Show, Eq)
assertThat (Foo "someName" 15) $
isEqualTo (Foo "someN1ame" 15)
. focus age
. tag "age"
. isGreaterThan 20
result in
given Foo {name = "someName", age = 15} should be equal to Foo {name = "someN1ame", age = 15}
Foo {name = "someName", age = 15}
╷
│
╵
Foo {name = "someN1ame", age = 15}
▲
[age] given 15 should be greater than 20
Examples
Simple assertion
result = 10
assertThat result $ isEqual 10
Composing assertion
Assertions are composable, this allows verifying multiple conditions during one test case.
result = 10
assertThat result $
isGreaterThan 5
. isLowerThan 20
>>> given 4 should be greater than 5
Focusing on part of data structure
Sometimes it is convenient to transform the subject under test and execute assertions on the extracted part of it. For this purpose, we have a focus
function.
data Foo = Foo {name :: String, age :: Int} deriving (Show, Eq)
assertThat (Foo "someName" 15) $
isEqualTo (Foo "someName" 15)
. focus age
. isGreaterThan 20
. isLowerEqualThan 5
>>> given 15 should be greater than 20
>>> given 15 should be lower or equal to 5
Changing subject uder test
The focus
function allows to transform the subject under test, but the original subject is lost. Function inside
is similar to the function focus
, but preserve theoriginal subject under test.
data Foo = Foo {name :: String, age :: Int} deriving (Show, Eq)
assertThat (Foo "someName" 15) $
inside age (isGreaterThan 20 . isLowerEqualThan 5)
. focus name
. isEqualTo "someName1"
>>> given 15 should be greater than 20
>>>
>>> given 15 should be lower or equal to 5
>>>
>>> given "someName" should be equal to "someName1"
>>> "someName"
>>> ╷
>>> │
>>> ╵
>>> "someName1"
>>> ▲
Tagging assertions
Once our test grows, it is hard to spot which assertions failed and why. That is why function tag
exists, one can name assertion and give it a more readable name for failure message.
data Foo = Foo {name :: String, age :: Int} deriving (Show, Eq)
assertThat (Foo "someName" 15) $
inside age (tag "age" . isGreaterThan 20 . isLowerEqualThan 5)
. tag "name"
. focus name
. isEqualTo "someName1"
. tag "should not be equal"
. isNotEqualTo "someName"
>>> [age] given 15 should be greater than 20
>>>
>>> [age] given 15 should be lower or equal to 5
>>>
>>> [name] given "someName" should be equal to "someName1"
>>> "someName"
>>> ╷
>>> │
>>> ╵
>>> "someName1"
>>> ▲
>>>
>>> [name.should not be equal] given "someName" should be not equal to "someName"
Custom assertions
It is sometimes convenient to create a custom assertion which explicitly describes what is testing. For this purpose we have simpleAssertion
function
isSuitableForEmployment :: Assertion Foo
isSuitableForEmployment =
simpleAssertion (\a -> age a > 17) (\a -> "new employee must be 18 years or older, but it has " <> show (age a))
. simpleAssertion (\a -> age a < 70) (\a -> "must be younger than 70 years old, but it has " <> show (age a))
assertThat (Foo "someName" 15) isSuitableForEmployment
>>> new employee must be 18 years or older, but it has 15
assertThat (Foo "someName" 76) isSuitableForEmployment
>>> must be younger than 70 years old, but it has 76
assert4hs-tasty - assert4hs provider for tasty