-
Notifications
You must be signed in to change notification settings - Fork 129
Tabular facts
Sometimes it's convenient to illustrate a fact with a table of values. For example, Conway's Game of Life has rules for when cells are born, stay alive, or die. They look nice expressed as a table:
(tabular
(fact "The rules of Conway's life"
(alive? ?cell-status ?neighbor-count) => ?expected)
?cell-status ?neighbor-count ?expected
:alive 1 FALSEY ; underpopulation
:alive 2 truthy
:alive 3 truthy
:alive 4 FALSEY ; overpopulation
;; A newborn cell has three parents
:dead 2 FALSEY
:dead 3 truthy
:dead 4 FALSEY)
Note that you can capitalize falsey
. FALSEY
and TRUTHY
are synonyms for the lowercase form. I find that using the capitalized form for one makes a tabular fact easier to read.
In case of a failure, the line number points to the prediction, as usual. But it's augmented by a description of which set of values caused the failure. For example, if alive?
always returns false, the fact above will yield three failures:
FAIL at (t_sweet.clj:237)
With table substitutions: {?cell-status :alive, ?neighbor-count 2, ?expected truthy}
Actual result did not agree with the checking function.
Actual result: false
Checking function: truthy
FAIL at (t_sweet.clj:237)
With table substitutions: {?cell-status :alive, ?neighbor-count 3, ?expected truthy}
Actual result did not agree with the checking function.
Actual result: false
Checking function: truthy
FAIL at (t_sweet.clj:237)
With table substitutions: {?cell-status :dead, ?neighbor-count 3, ?expected truthy}
Actual result did not agree with the checking function.
Actual result: false
Checking function: truthy
Substitutions work with more than just plain values. For example, instead of using truthy
and falsey
, you can substitute the checking arrow:
(tabular
(fact "only two numbers have the same sum and square"
(* ?n ?n) ?arrow (+ ?n ?n))
?n ?arrow
0 =>
2 =>
;; Failure cases
1 =not=>
;; and so on
)
Tabular facts behave exactly like regular facts. They can be turned into future facts:
(tabular
(future-fact (inc ?int) => ?int)
?int
1)
They obey fact-wide prerequisites, setup/teardown, and so on.
You can have many checkables within the fact.
Doc strings (and other metadata) can be put with either the tabular
or the fact
.
(tabular "increment works"
(fact (inc ?int) => ?int)
?int
1)
There are cases where Midje cannot distinguish between the header and body of a tabular
. In these cases your test may mix values between columns.
As of Midje 1.9.3-alpha2
you can explicitly delineate between the header and body by using square brackets i.e. [
/]
.
(fact "explicit syntax for the tabular header, to deal with `aa` being unbound during macro expansion time"
(let [aa 2
aaa 3]
(tabular
(fact (?f ?x) => ?res)
[?x ?f ?res] ;; <- note the use of `[` and `]`
aa inc 3
aaa inc 4)))
Without the explicit square brackets you would see an error where aa
is used in the place of ?f
instead of ?x
It is advised that you adhere to this syntax if you are on 1.9.3
or above, since it will prevent this bug from popping up.