Liking cljdoc? Tell your friends :D

Expecting More

So far we've seen expectations with just a single value or predicate being used to test one or more result values. We will often want to expect several things about our results, and Expectations provides a way to articulate that without writing multiple checks that duplicate the expression being tested.

expect more

If you have multiple predicates that you expect to be satisfied by a given expression, you can use more to combine them into a single expectation:

  (expect (more vector? not-empty) [1 2 3])

This expects the (actual) test value to be a vector and also to be non-empty (we could have specified seq there just as easily). This can be particularly powerful when combined with from-each to check that multiple expectations hold for computations applied to multiple input values:

  (expect (more vector? not-empty)
          (from-each [n [1 2 3]]
            (into [] (range n))))

If you have expectations that should hold for all input values, you might want to look at clojure.test.check instead.

expect more->

If you have a series of predicates (or values) that you expect to be satisfied by a given expression after a certain amount of preprocessing, you can use the threaded version -- more-> -- to express that:

  (expect (more-> 1 first
                  3 last)
          [1 2 3])

more-> accepts a series of predicate (or value) and expression pairs. The (actual) test value is threaded through each of the expressions and the predicate (or value) is expected of the result.

Going back to our lookup-membership example, we might want to expect:

  (expect (more-> #{:membership/id :membership/status :membership/type ,,,}
                  (-> keys set)
                  pos?       :membership/id
                  "active"   :membership/status
                  "platinum" :membership/type)
          (lookup-membership db-spec test-user))

Since the test value is threaded-first into the expressions, we can use -> to further thread the value into additional processing steps, such as getting the sequence of keys and turning that into a set for the expectation of which keys should be returned in a membership hash map. In addition, we expect that (-> actual :membership/id) is positive pos? and that certain other keys have specific values.

clojure.test provides thrown-with-msg? as a way to assert both the type of exception thrown and a regular expression that should apply to the message in that exception. more-> allows us to do that in a more general way:

  (is (thrown-with-msg? ArithmeticException #"Divide by zero" (/ 1 0)))
  (expect (more-> ArithmeticException type
                  #"Divide by zero"   ex-message)
          (/ 1 0))

See below for a more comprehensive example of exception testing that also uses more-of.

expect more-of

Sometimes destructuring an (actual) test value is the easiest way to apply your expectations:

  (expect (more-of {:membership/keys [id status type] :as data}
                   ,,,
                   pos?       id
                   "active"   status
                   "platinum" type)
          (lookup-membership db-spec test-user))

The expectation on the set of keys has been omitted here to highlight how the destructuring may simplify the other expectations, but it would be:

                   #{:membership/id :membership/status :membership/type ,,,}
                   (set (keys data))

Some simpler examples (taken from Expectations' original documentation):

  (expect (more-of x
                   vector? x
                   1       (first x))
          [1 2 3])
  (expect (more-of [x :as all]
                   vector? all
                   1       x)
          [1 2 3])

more-of can be used with from-each to provide functionality similar to are in clojure.test (but more powerful):

  (deftest are-example
    (are [expected start end]
         (= expected (range start end))
         [0 1 2 3] 0 4
         []        0 0
         [1 2 3]   1 4))

  (defexpect equivalent-to-are
    (expect (more-of [expected actual]
                     expected actual)
            (from-each [[expected start end]
                        [[[0 1 2 3] 0 4]
                         [[]        0 0]
                         [[1 2 3]   1 4]]]
              [expected (range start end)])))

Although this is more verbose for this basic example, remember that the expected value could also be a predicate function, a regex, a Spec, etc.

more-of can also be used with more-> to provide succinct tests on Clojure's ex-info exceptions:

  (defexpect ex-info-tests
    (expect (more-> clojure.lang.ExceptionInfo type
                    (more-of {:keys [status responseCode]}
                             409 status
                             4001110 responseCode) ex-data)
            (throw (ex-info "boo" {:status 409 :responseCode 4001110}))))

In this example, the exception is threaded into type and the predicate is a class, and it is also threaded into ex-data and the predicate is a more-of expression that destructures that data and matches parts of it.

Further Reading

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close