Skip to content

Commit

Permalink
Improve documentation and tests cases
Browse files Browse the repository at this point in the history
  • Loading branch information
gilvan-reis committed Jan 25, 2024
1 parent 1db9ff4 commit ccf40b4
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 25 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ for a specific value, e.g.
- Note: Given that the default matcher for maps is `embeds`, nested maps continue being matched with embeds (instead of also being matched with `equals`). Check out 'Overriding default matchers' below for instructions on how to match nested maps with equals too.
- sequence: matches when the `expected` sequences's matchers match the given sequence. Similar to midje's `(just expected)`
- set: matches when all the elements in the given set can be matched with a matcher in `expected` set and each matcher is used exactly once.
- `strictly equals`: overrides map's default matchers to `equals`, using it for nested structures (see Overriding default matchers, below)
- `embeds` operates over maps, sequences, and sets
- map: matches when the map contains some of the same key/values as the `expected` map.
- sequence: order-agnostic matcher that will match when provided a subset of the `expected` sequence. Similar to midje's `(contains expected :in-any-order :gaps-ok)`
Expand Down Expand Up @@ -264,11 +265,11 @@ For example, if you want to do exact map matching you need to use a log of `m/eq
{:a {:b {:c 1 :extra-c 0} :extra-b 0} :extra-a 0})))
```

This verbosity can be avoided by redefining the matcher data-type defaults using the `match-with` matcher:
For convenience we've also added the built-in matcher `strictly-equals` to reduce this verbosity:

``` clojure
(deftest exact-map-matching-with-match-with
(is (match? (m/match-with [map? m/equals] {:a {:b {:c odd?}}}))
(is (match? (m/strictly-equals {:a {:b {:c odd?}}}))
{:a {:b {:c 1}}}))
```

Expand Down
16 changes: 10 additions & 6 deletions src/cljc/matcher_combinators/matchers.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@
(declare match-with)

(defn strictly-equals
"Return a matcher for the expected value which matches nested maps with `equals`.
This solves a common need of matching nested maps with strict equality.
See also: `match-with`.
"A matcher that always uses the `equals` matcher at every level of nesting.
Usage:
(is (match? (strictly-equals {:a :b})
{:a :b :c :d}))"
Useful given that matchers usually only change the first level of the data
they are applied to, leaving nested data to use the default matcher of that
type of data. For instance, this can be used to assert that any nested map
has exactly the same keys and matching values as provided in the `expected`,
and no more.
Note: this excludes functions, which continue to be invoked
as predicates, and regex, which continue to be invoked with `regex`,
instead of being compared via the `equals` matcher."
[expected]
(match-with [map? equals] expected))

Expand Down
61 changes: 44 additions & 17 deletions test/clj/matcher_combinators/matchers_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
[matcher-combinators.matchers :as m]
[matcher-combinators.result :as result]
[matcher-combinators.test :refer [match?]]
[matcher-combinators.test-helpers :as test-helpers :refer [no-match? abs-value-matcher]])
(:import [matcher_combinators.model Mismatch Missing InvalidMatcherContext InvalidMatcherType]))
[matcher-combinators.test-helpers :refer [abs-value-matcher no-match?]])
(:import [matcher_combinators.model InvalidMatcherContext InvalidMatcherType Mismatch Missing]))

(defn any? [_x] true)

Expand Down Expand Up @@ -362,21 +362,48 @@
actual)))))

(deftest strictly-equals-matcher
(testing "passing case with one level map"
(is (match? (m/strictly-equals {:a :b})
{:a :b})))

(testing "passing case with multi level map"
(is (match? (m/strictly-equals {:a :b :c {:d {:e :f}}})
{:a :b :c {:d {:e :f}}})))

(testing "failing case with one level map"
(is (no-match? (m/strictly-equals {:a :b})
{:a :b :c :d})))

(testing "failing case with multi level map"
(is (no-match? (m/strictly-equals {:a :b :c {:d {:e :f}}})
{:a :b :c {:d :e}}))))
(testing "nested maps"
(testing "passing case"
(is (match? (m/strictly-equals {:user1 {:id 5 :name "hennix"}
:user2 {:id 3 :name "flynt"}})
{:user1 {:id 5 :name "hennix"}
:user2 {:id 3 :name "flynt"}})))

(testing "`strictly-equals` fails when nested maps have extra keys"
(is (no-match? (m/strictly-equals {:user1 {:id 5}
:user2 {:id 3}})
{:user1 {:id 5 :name "hennix"}
:user2 {:id 3 :name "flynt"}})))

(testing "`equals` still matchs when nested maps have extra keys"
(is (match? (m/equals {:user1 {:id 5}
:user2 {:id 3}})
{:user1 {:id 5 :name "hennix"}
:user2 {:id 3 :name "flynt"}})))

(testing "`strictly-equals` is similar to using `equals` in each map"
(is (no-match? (m/equals {:user1 (m/equals {:id 5})
:user2 (m/equals {:id 3})})
{:user1 {:id 5 :name "hennix"}
:user2 {:id 3 :name "flynt"}}))))

(testing "functions"
(testing "`strictly-equals` does not apply `equals` to functions"
(is (match? (m/strictly-equals {:x odd?})
{:x 1})))

(testing "`equals` should be aplied directly to the function to fail"
(is (no-match? (m/equals {:x (m/equals odd?)})
{:x 1}))))

(testing "regex"
(testing "`strictly-equals` does not apply `equals` to regex"
(is (match? (m/strictly-equals {:x #"\s+"})
{:x "abc"})))

(testing "`equals` should be aplied directly to the regex to fail"
(is (no-match? (m/equals {:x (m/equals #"\s+")})
{:x "abc"})))))

(def gen-processable-double
(gen/double* {:infinite? false :NaN? false}))
Expand Down

0 comments on commit ccf40b4

Please sign in to comment.