Skip to content

Commit

Permalink
Same type for asserting occurrences of same value (#25)
Browse files Browse the repository at this point in the history
* Same type for asserting occurrences of same value

When you have an unknown value that needs to repeat in the expected
assertion you don't have a lot of good options. You can let the result,
and pull out as many values as you want to check but this quickly gets
cumbersome, where the structure of actual needs to be declared twice,
once to pull out the values to check and again in expected.

In this case, we want to assert, that `<id1>` and `<id2> are the same
across the actual value. We can do this with `=?/same`

```
(let [actual [{:id <id1> :ref [:field <id1>]}
              {:id <id2> :ref [:field <id1>]}]
      id1 (get-in actual [0 :id])
      id2 (get-in actual [1 :id])]
  (is (=? [{:id id1 :ref [:field id1]} {:id id2 :ref [:field id2] actual)))

(let [actual [{:id <id1> :ref [:field <id1>]}
              {:id <id2> :ref [:field <id1>]}]]
  (is (=? [{:id (=?/same :id1) :ref [:field (=?/same :id1)]}
           {:id (=?/same :id2) :ref [:field (=?/same :id2)]}]
           actual)))
```

* Address pr feedback
  • Loading branch information
snoe authored May 6, 2024
1 parent 490c9b0 commit ac3c663
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
46 changes: 45 additions & 1 deletion src/mb/hawk/assert_exprs/approximately_equal.clj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
"Whether to enable Methodical method tracing for debug purposes."
false)

(def ^:private ^:dynamic *same*
nil)

(defn =?-diff*
"Are `expected` and `actual` 'approximately' equal to one another?"
([expected actual]
Expand All @@ -41,7 +44,8 @@
(let [diff-fn (if (map? diff-fn)
(add-primary-methods diff-fn)
diff-fn)]
(binding [=?-diff diff-fn]
(binding [=?-diff diff-fn
*same* (atom {})]
(if *debug*
(methodical/trace diff-fn expected actual)
(diff-fn expected actual))))))
Expand Down Expand Up @@ -215,3 +219,43 @@
epsilon (.epsilon this)]
(when-not (algo.generic.math/approx= expected actual epsilon)
(list 'not (list 'approx expected actual (symbol "#_epsilon") epsilon)))))

(deftype Same [k])

(defn same
"Used inside a =? expression. Checks that all occurrences of the same [[k]] value are equal.
On the first occurrence of `(same k)`, it saves the actual value under [[k]].
All other occurrences of `(same k)` are expected to be equal to that saved value.
```
(is (?= [(same :id) (same :id)}] [1 1])) ; => true
(is (?= [(same :id) (same :id)}] [1 2])) ; => false
```"
[k]
(->Same k))

(defmethod print-dup Same
[^Same this ^java.io.Writer writer]
(.write writer (format "(same %s)" (pr-str (.k this)))))

(defmethod print-method Same
[this writer]
((get-method print-dup Same) this writer))

(defmethod pprint/simple-dispatch Same
[^Same this]
(pprint/pprint-logical-block
:prefix "(same " :suffix ")"
(pprint/write-out (.k this))))

(methodical/defmethod =?-diff [Same :default]
[^Same this actual]
(when *same*
(if (contains? @*same* (.k this))
(let [previous-value (get @*same* (.k this))]
(when-not (= previous-value actual)
(list 'not= (symbol "#_") (list 'same (.k this)) previous-value actual)))
(do
(swap! *same* assoc (.k this) actual)
nil))))
22 changes: 22 additions & 0 deletions test/mb/hawk/assert_exprs/approximately_equal_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,25 @@
(testing "nil should not match the =?/approx method -- fall back to the :default"
(is (= "(not= (approx [1 0.1]) nil)"
(pr-str (=?/=?-diff (=?/approx [1 0.1]) nil)))))))

(deftest ^:parallel same-test
(testing "pr-str"
(is (= "(same :a)" (pr-str (=?/same :a)))))
(testing "Same does nothing with 1 occurrence"
(is (=? (=?/same :a) 1)))
(testing "Multiple occurrences are the same"
(is (=? [(=?/same :a) (=?/same :b) (=?/same :b) (=?/same :a)] [1 2 2 1])))
(testing "Works nested"
(is (=? [(=?/same :a) {:nested (=?/same :a)}] [1 {:nested 1}])))
(testing "Works on complex values"
(is (=? [(=?/same :a) {:nested (=?/same :a)}] [#{1 2 3} {:nested #{1 2 3}}])))
(testing "When not the same"
(is (= "[nil (not= #_ (same :a) 1 2)]"
(pr-str (=?/=?-diff* [(=?/same :a) (=?/same :a)] [1 2]))))
(is (= "[nil (not= #_ (same :a) 1 2) nil (not= #_ (same :b) 2 3)]"
(pr-str (=?/=?-diff* [(=?/same :a) (=?/same :a)
(=?/same :b) (=?/same :b)]
[1 2 2 3]))))
(testing "Not the same and nested"
(is (= "[nil {:nested (not= #_ (same :a) 1 2)}]"
(pr-str (=?/=?-diff* [(=?/same :a) {:nested (=?/same :a)}] [1 {:nested 2}])))))))

0 comments on commit ac3c663

Please sign in to comment.