Skip to content

Tabular facts

Phillip Mates edited this page Sep 28, 2018 · 12 revisions

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

Details

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)

Explicit syntax

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.

Clone this wiki locally
You can’t perform that action at this time.