A helper to save lines of code in tests. Provides a number of objects with a
fake (forged) equality logic. Say, the any.core/uuid object equals to any
UUID. The same approach for strings, numbers, and so on.
(ns demo
(:require
[any.core :as any]))
;; positive cases
(= any/uuid (random-uuid)) ;; true
(= any/string "whatever") ;; true
(= any/keyword :dunno) ;; true
;; negative cases
(= any/uuid 1) ;; false
(= any/string nil) ;; false
(= any/symbol :keyword) ;; false
Custom equality works for nested items in maps, vectors and other collections:
(= {:id any/uuid :name "Ivan"}
{:id (random-uuid) :name "Ivan"})
(= [any/uuid any/string]
(list (random-uuid) "test"))
The library provides various objects for equality: Clojure primitives, Java
classes from java.time.* and other packages, regex checking, ranges, and more
(see below).
Moslty for tests, as it helps a lot. Imagine you're testing a function returning a map:
(deftest test-some-func
(is (= {:this 1
:that [:foo :bar]
:more {:ok true}}
(some-function 1 "abc" [:foo]))))
All fields are constant so there is nothing to worry about. A regular =
operator works fine.
Should some-function return a map with a random UUID, you cannot blindly
compare maps with =. At this moment, various solutions come into play. You may
dissoc all random fiels and compare only the constant subpart:
(deftest test-some-func
(is (= {:this 1
:that [:foo :bar]
:more {:ok true}}
(-> (some-function 1 "abc" [:foo])
(dissoc :id :created_at)))))
But you still need to ensure the result has both :id and :created_at
though. That leads to clumsy code:
(deftest test-some-func
(let [result (some-function 1 "abc" [:foo])]
(is (= {:this 1
:that [:foo :bar]
:more {:ok true}}
(dissoc result :id :created_at)))
(is (uuid? (:id result)))
(is (instance? java.time.Instant (:created_at result)))))
Any achieves the same in a shorter way:
(deftest test-some-func
(is (= {:id any/uuid
:created_at any/Instant
:this 1
:that [:foo :bar]
:more {:ok true}}
(some-function 1 "abc" [:foo]))))
This reminds Clojure Spec or Malli, yet partially. Schemas usually check types
but not values. With Any, you obtain both: check values and when they're
random or too complex, rollback to types.
I can hear you're saying "use a matching library" like core.match,
matchete, machto, strucjure, match.this, match.that, and so on. I
know. But they're complex, and I don't like complex things. Any is as dull
as ever possible. You don't have to rewrite your tests, only replace some
values with any/something.
Lein:
[com.github.igrishaev/any "0.1.0"]
Deps.edn
com.github.igrishaev/any {:mvn/version "0.1.0"}
Clojure primitives:
any/stringany/uuidany/uuid-string (a string matching UUID pattern)any/int (short, integer, long)any/float (float, double)any/keywordany/symbolany/not-nil (like some?)any/vectorany/listany/setany/mapParameter-depended objects:
| Object | Comment |
|---|---|
(any/instance SomeClass) | check if a value is an instance of SomeClass |
(any/enum :foo :bar: :baz) | check if a value if one of the variadic arguments |
(any/enum-seq [:foo :bar: :baz]) | like enum but accepts a collection of items |
(any/range 1 9) | if a given number is in range [1, 9) |
(any/re-matches #"some regex") | if a value is a string matching the pattern |
(any/re-find #some regex) | if a value is a string where the pattern can be found |
(any/includes "substring") | check if a string includes a substring |
(any/starts-with "substring") | check if a string starts with a substring |
(any/ends-with "substring") | check if a string ends with a substring |
(any/count 32) | check if an object has exact 32 items |
Examples:
(= (any/range 1 9) 3) ;; true
(= (any/range 1 9) 9) ;; false
(= (any/enum 1 :foo 9 "test") 9) ;; true
(= (any/re-matches #"\s+foobar\s+") " foobar ") ;; true
(= (any/re-find #"\d+") " foo42bar ") ;; true
(= (any/includes "foo") "aa foo bb") ;; true
(= (any/count 3) [1 2 3]) ;; true
Java instances:
any/LocalDateany/LocalDateTimeany/LocalTimeany/OffsetTimeany/OffsetDateTimeany/Instantany/ZonedDateTimeany/Periodany/Fileany/InputStreamany/OutputStreamany/Readerany/Writerany/Dateany/UUIDJava arrays:
any/bytesany/intsany/shortsany/longsany/floatsany/doublesany/booleansany/charsany/objectsEvery Any object has a custom .toString and print-method
implementations. This makes the output a bit clearer in tests:
(is (= {:id any/uuid :name "Ivan"}
{:id 42 :name "Ivan"}))
FAIL in () (form-init4968882668721322291.clj:227)
expected: {:id <any UUID>, :name "Ivan"}
actual: {:id 42, :name "Ivan"}
(is (= {:result {:data {:created_at any/Instant}}}
{:result {:data {:created_at "bad string"}}}))
FAIL in () (form-init4968882668721322291.clj:235)
expected: {:result {:data {:created_at <any instance of java.time.Instant>}}}
actual: {:result {:data {:created_at "bad string"}}}
Keep in mind that the standard = Clojure function relies on equivalence, not
equality. Equivalence is a custom way to compare objects in Clojure. Say,
instances of ArrayList and PersistentList are equivalent if they are of the
same size and each Nth element is equivalent to its counterpart.
Prior to 0.1.1, Any provided (reify Object) objects with a custom equals
method. Since 0.1.1, it provides (reify IPersistentCollection) with the
overridden equiv method. This approach gives more freedom and open doors for
collections.
When comparing Any objects with values, the order matters. Objects provided by
Any should go first:
;; like this
(= any/text "hello")
;; NOT like this
(= "hello" any/text)
These two forms expand into the following:
;; like this
(.equiv any/text "hello")
;; NOT like this
(.equals "hello" any/text )
In the first case, the any/text object has got its own logic checking if the
opposite object is a string. In the second case, the standard String.equals
method is called.
Any objects are built with a number of utility macros, and you can reuse
them. The any/instance accepts a class and returns an object which equals to
instances of that class only:
(import 'com.acme.Class)
(def AnyClass
(any/instance com.acme.Class))
(= AnyClass value)
The any macro accepts a binding symbol, a text representation and a custom
body with the logic of equality:
(any/any [x "text for (.toString) or (print)"]
(boolean (some-condition x)))
The body should always return true or false, but not nil. It's better to wrap
the result with boolean to prevent such cases.
The idea of making Any originally comes from this blog post: "A fake Clojure
Object equals to what you want".
©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©
Ivan Grishaev, 2026. © UNLICENSE ©
©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©©
Can you improve this documentation? These fine people already did:
Ivan Grishaev & Artem MakarovEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |