Matchers library

Here you will find all matchers, supported by CL-HAMCREST, grouped by their purpose.

Object matchers

This kind of matchers checks some sort of properties on an object. In this case objects are not only the CLOS objects but also, hashmaps, alists and property lists.

macro (has-plist-entries &rest entries)

Matches plist entries:

TEST> (let ((obj '(:foo :bar)))
        (assert-that obj
                     (has-plist-entries :foo "bar"
                                        :blah "minor")))
  × Key :FOO has :BAR value, but "bar" was expected

This way you can test any number of plist’s entries.

macro (hasnt-plist-keys &rest keys)

Checks if given keys are missing from an object:

TEST> (let ((obj '(:foo "bar")))
        (assert-that obj
                     (hasnt-plist-keys :blah :minor)))
  ✓ Keys :BLAH, :MINOR are absent

Assertion fails if at least one key is present in the object:

TEST> (let ((obj '(:foo "bar")))
        (assert-that obj
                     (hasnt-plist-keys :blah :foo)))
  × Key :FOO is present in object, but shouldn't.
macro (has-alist-entries &rest entries)

Matches alist entries:

TEST> (let ((obj '((:the-key . "value"))))
        (assert-that obj
                     (has-alist-entries :the-key "value")))

  ✓ Has alist entries:
      :THE-KEY = "value"

TEST> (let ((obj '((:the-key . "value"))))
        (assert-that obj
                     (has-alist-entries :the-key "value"
                                        :missing-key "value")))

  × Key :MISSING-KEY is missing

TEST> (let ((obj '((:the-key . "value"))))
        (assert-that obj
                     (has-alist-entries :the-key "other-value")))

  × Key :THE-KEY has "value" value, but "other-value" was expected
macro (has-hash-entries &rest entries)

Matches hash entries:

TEST> (let ((obj (make-hash-table)))
        (setf (gethash 'the-key obj) "value")
        (assert-that obj
                     (has-hash-entries 'the-key "value")))

  ✓ Has hash entries:
      THE-KEY = "value"

TEST> (let ((obj (make-hash-table)))
        (setf (gethash 'the-key obj) "value")
        (assert-that obj
                     (has-hash-entries 'missing-key "value")))

  × Key MISSING-KEY is missing

TEST> (let ((obj (make-hash-table)))
        (setf (gethash 'the-key obj) "value")
        (assert-that obj
                     (has-hash-entries 'the-key "other-value")))

  × Key THE-KEY has "value" value, but "other-value" was expected
macro (has-properties &rest entries)

Matches object properties:

TEST> (defvar the-object)
THE-OBJECT
TEST> (setf (getf the-object :tags) '(one two))
TEST> (assert-that 'the-object
                   (has-properties :tags '(one two)))
  ✓ Has properties:
      :TAGS = (ONE TWO)

TEST> (assert-that 'the-object
                   (has-properties :tags 'wrong-value))
  × Property :TAGS has (ONE TWO) value, but WRONG-VALUE was expected

TEST> (assert-that 'the-object
                   (has-properties :missing-property '(one two)))
  × Property :MISSING-PROPERTY is missing
macro (has-slots &rest entries)

Matches object slots:

TEST> (defstruct task
        title
        description)

TEST> (defvar task (make-task :title "The title "))

TEST> (assert-that task
                   (has-slots 'title "The title "))
  ✓ Has slots:
      TITLE = "The title "

TEST> (assert-that task
                   (has-slots 'title "Wrong title "))
  × Slot TITLE has "The title " value, but "Wrong title " was expected

TEST> (assert-that task
                   (has-slots 'description nil))
  ✓ Has slots:
      DESCRIPTION = NIL
function (has-type expected-type)

Checks if a list have specivied length.

TEST> (matcher-description (has-type 'cons))
"Has type CONS"

TEST> (funcall (has-type 'cons) 100500)
; Debugger entered on #<ASSERTION-ERROR 100500 has type (INTEGER 0 4611686018427387903), but CONS was expected>

Sequence matchers

function (has-length expected-length)

Checks if a list have specivied length.

TEST> (assert-that 'nil (has-length 0))
  ✓ Has length of 0

TEST> (assert-that '(a b c d) (has-length 4))
  ✓ Has length of 4

TEST> (assert-that '(a b c d) (has-length 100500))
  × List (A B C D) has length of 4, but 100500 was expected
macro (contains &rest entries)

Checks if each item from a list matches to given matchers.

Contains can accept as raw values, as another matchers:

TEST> (assert-that '(:foo
                     (a b c)
                     d)
                   (contains :foo
                             (has-length 3)
                             'd))
  ✓ Contains all given values

Given list should have a length equal to count of matchers:

TEST> (assert-that '(:foo
                     (a b c)
                     d)
                   (contains :foo))
  × Expected value is shorter than result

You can ignore value of some list items, by using (any) matcher:

TEST> (assert-that '(:foo
                     (a b c)
                     d)
                   (contains :foo (any) (any)))
  ✓ Contains all given values
macro (contains-in-any-order &rest entries)

Same as contains, but items in the sequence can be in any order:

TEST> (assert-that '(:foo
                     (a b c)
                     d)
                   (contains-in-any-order
                    (has-length 3)
                    'd
                    :foo))

  ✓ Contains all given values

Boolean matchers

function (any)

Assertion is passed regardles of value of the object:

TEST> (assert-that 1 (any))
  ✓ Any value if good enough

TEST> (assert-that "the-string" (any))
  ✓ Any value if good enough

TEST> (assert-that 'the-symbol (any))
  ✓ Any value if good enough

TEST> (assert-that '(1 2 3) (any))
  ✓ Any value if good enough
function (has-all &rest matchers)

Makes a matcher which groups another matchers with AND logic.

This way we can check if plist has one key and hasn’t another. And if all matchers succeed, then has-all succeed as well:

TEST> (assert-that '(:foo "bar")
                   (has-all (has-plist-entries :foo "bar")
                            (hasnt-plist-keys :blah)))
  ✓ All checks are passed

If at least one check is failed, then has-all fails too:

TEST> (assert-that '(:foo "bar" :blah "minor")
                   (has-all (has-plist-entries :foo "bar")
                            (hasnt-plist-keys :blah)))
  × Key :BLAH is present in object, but shouldn't

Utility functions

function (matcher-description fn)

Returns description of a given matcher function.

Can be used to print nested matchers in a nicely indented, human readable way:

TEST> (matcher-description (has-length 100500))
"Has length of 100500 "

TEST> (matcher-description (contains
                            (has-plist-entries :foo "bar ")
                            (has-plist-entries :foo "minor ")))
"Contains all given values "

TEST> (matcher-description (has-plist-entries
                            :foo "bar "
                            :blah (has-hash-entries :minor "again ")))
"Has plist entries:
  :FOO = "bar"
  :BLAH = Has hash entries:
            :MINOR = "again""