-
Notifications
You must be signed in to change notification settings - Fork 129
Checking sequential collections
The checkers relevant to sequential collections are just
, contains
, has-prefix
, has-suffix
, has
and n-of
. They apply to strings, too, but the details are more helpfully explained separately.
A sequential collection on the right-hand side of a prediction is checked using equality. As is typical for Clojure, that means that lists and vectors and lazy sequences can all be equal to one another:
(fact
'(1 2 3) => [1 2 3]
[1 '(2) 3] => '(1 [2] 3)
(map inc (range 0 3)) => [1 2 3])
If you want a comparison that uses extended equality instead of equality, you can use just
:
(fact "just uses extended equality"
[1 2 3] => (just [odd? even? odd?])
["a" "aa" "aaa"] (just [#"a+" #"a+" #"a+"]))
As a side note, the second prediction can be more economically written like this:
(fact ["a" "aa" "aaa"] => (three-of #"a+"))
three-of
(more generally, n-of
, is covered below.)
Since just
always takes a collection, wrapping the expected collection in brackets or quoted parentheses is redundant. So they can be omitted.
(fact "when `just` takes no options, you can omit brackets"
[1 2 3] => (just odd? even? odd?))
Whether you do that is very much a matter of taste.
just
isn't as convenient for trees as it is for flat lists because extended equality in just
is not recursive. That is:
(fact "extended equality only applies to the top level"
[[[1]]] =not=> (just [[[odd?]]]))
If you want extended equality to apply to embedded structures, you have to use just
(or other checkers) at each level:
(fact "you have to do this"
[[[1]]] => (just (just (just odd?))))
At some point, I hope to see checkers tailored to make terse expected results for tree structures, but they don't exist yet.
It's sometimes the case that you care about the contents of the sequence, but not the order. You can do that by using a set in just
:
(fact [2 1 3] => (just #{1 2 3}))
... but it's perhaps better to be explicit with the :in-any-order
flag:
(fact "you can specify that order is irrelevant"
[2 1 3] => (just [1 2 3] :in-any-order))
(Note: you can leave out the brackets in this case as well, but that looks creepy to me.)
Midje goes to some effort not to be fooled by overcommitting to matches. For example, consider this:
(fact [1 3] => (just [odd? 1] :in-any-order))
If Midje decided that the actual value 1
matched odd?
, then a mismatch of 3
and the 1
after odd?
would lead to a spurious failure. But Midje correctly discovers the match in the alternate order.
contains
searches for a matching subsequence of the actual results:
(fact "contains requires only a subset to match"
[1 2 3] => (contains even? odd?))
(Note that contains
, like just
, allows you to omit brackets.)
contains
searches for a contiguous sequence of matches. That is:
(fact [1 2 3] =not=> (contains odd? odd?))
If you want to relax that requirement, use :gaps-ok
:
(fact [1 2 3] => (contains [odd? odd?] :gaps-ok))
contains
, like just
, supports :in-any-order
. Here's an example that uses both it and :gaps-ok
.
(fact [5 1 4 2] => (contains [1 2 5] :gaps-ok :in-any-order))
You can also use a set if you want to be more terse:
(fact [5 1 4 2] => (contains #{1 2 5} :gaps-ok))
Note: the algorithm used for this complicated case is inefficient and also won't work on longer sequences. I keep hoping someone will replace it.
Whereas contains
finds matches anywhere within the sequence, has-prefix
forces the match to be at the beginning:
(fact "has-prefix is anchored to the left"
[1 2 3] =not=> (has-prefix [2 3]) ; it's not a prefix
[1 2 3] => (has-prefix [1 2])
[1 2 3] =not=> (has-prefix [2 1]) ; order matters
[1 2 3] => (has-prefix [2 1] :in-any-order)
[1 2 3] => (has-prefix #{2 1}))
has-suffix
is like has-prefix
, except the match has to be at the very end.
These are used to avoid giving just
a sequence of n identical elements:
(fact "one checker to match exactly N elements."
["a"] => (one-of "a")
[:k :w] => (two-of keyword?)
["a" "aa" "aaa"] => (three-of #"a+")
;; ...
[1 3 5 7 9 11 13 15 17] => (nine-of odd?)
["a" "b" "c" "d" "e" "f" "g" "h" "i" "j"] => (ten-of string?)
;; to go above ten-of
(repeat 100 "a") => (n-of "a" 100))
Note that n-of
and friends use extended equality. So here's how you might check for a 2x2 array of even numbers:
(fact "the result is an even square"
[[2 4]
[6 8]] => (two-of (two-of even?)))
You can apply Clojure's quantification functions (every?
, some
, and so on) to all the values of a sequence:
(fact
[2 3 4] => (has some odd?)
[2 4 6] =not=> (has some odd?))
has
looks silly, in that it appears you can do the same with partial
:
(fact
[2 3 4] => (partial some odd?)
[2 4 6] =not=> (partial some odd?))
That indeed works correctly. There are two reasons to use has
instead:
-
You can use regular expressions, which match according to the rules of extended-equality.
(fact ["ab" "aab" "aaab"] => (has every? #"a+b")) (fact [1 2 3] => (has not-any? #"a+b"))
-
Some more elaborate uses of
partial
will not work as you expect:user=> (fact [[1] [2]] => (partial every? (just [1]))) true
This incorrectly checks out because of how the collection checkers report failures. That's a mistake in Midje's design, one that will be fixed. In the meantime, use
has
.