matcher-combinators

Phillip Mates 2026-06-18T07:30:22.458929Z

glad you're enjoying the library! could also do something like this (only implemented functionality for clojure.test; will need additional work if you want to use it in other contexts (cljs, standalone matching, etc)

(ns matcher-combinators.metaconstants
  "inspired by Midje's metaconstants
  ()

  in response to a question by @enn on Clojurians slack
  ()"
  (:require [matcher-combinators.test :refer [match?]]
            [matcher-combinators.result :as result]
            [matcher-combinators.model :as model]
            [matcher-combinators.core :as core]
            [clojure.test :refer [deftest is]]))

(def ^:dynamic *metaconstants* (atom {}))

;; store original implementations of clojure.test/assert-expr
(defonce assert-methods (methods clojure.test/assert-expr))

(defmethod clojure.test/assert-expr 'match? [msg form]
  `(binding [*metaconstants* (atom {})]
     ;; call original `match? implementation for clojure.test/assert-expr
    ~((get assert-methods 'match?) msg form)))

(defn metaconst
  "For every `(metaconst 'x)` value in the expected, asserts that the
  corresponding values in the `actual` are always `=`

  useful for asserting that two values in a nested structure are equivalent
  without asserting anything else about them"
  [v]
  (reify core/Matcher
    (-matcher-for [this] this)
    (-matcher-for [this _] this)
    (-match [_this actual]
      (if-let [existing-actual (get @*metaconstants* v)]
        (if (and existing-actual (= existing-actual actual))
          {::result/type   :match
           ::result/value  actual
           ::result/weight 0}
          {::result/type   :mismatch
           ::result/value  (model/->Mismatch existing-actual actual)
           ::result/weight 1})
        (do
          (swap! *metaconstants* assoc v actual)
          {::result/type   :match
           ::result/value  actual
           ::result/weight 0})))
    (-base-name [_] 'metaconstant)))


(deftest metaconstant-test
  (is (match? {:a (metaconst 'a)
               :a2 (metaconst 'a)
               :id1 (metaconst 'id)
               :id2 (metaconst 'other-id)}
              {:a 4 :a2 4
               :id1 (java.util.UUID/randomUUID)
               :id2 (java.util.UUID/randomUUID)})
      "the a/a2 values must be equal, but the id values don't need to be")

  (let [id (java.util.UUID/randomUUID)]
    (is (match? {:id1 (metaconst 'id)
                 :id2 (metaconst 'id)}
                {:id1 id
                 :id2 id})
        "the id values must match"))

  ;; expected to fail
  (is (match? {:a (metaconst 'a)
               :a2 (metaconst 'a)}
              {:a 5 :a2 4})))