|
2 | 2 |
|
3 | 3 | A `clojure.test`-compatible version of the [classic Expectations testing library](https://clojure-expectations.github.io/).
|
4 | 4 |
|
| 5 | +## What? |
| 6 | + |
| 7 | +This library brings `expect`, `more`, `more-of`, etc from Expectations into the |
| 8 | +`clojure.test` world to be used instead of (or in addition to) the familiar `is` |
| 9 | +macro. You can either use `deftest` from `clojure.test`, or `defexpect` from |
| 10 | +this library to wrap your tests. |
| 11 | + |
| 12 | +```clojure |
| 13 | +(ns my.cool.project-test |
| 14 | + (:require [clojure.test :refer [deftest is]] |
| 15 | + [expectations.clojure.test :refer :all])) |
| 16 | + |
| 17 | +;; mix'n'match libraries: |
| 18 | + |
| 19 | +(deftest mixed |
| 20 | + (is 2 (+ 1 1)) |
| 21 | + (expect even? (+ 1 1))) |
| 22 | + |
| 23 | +;; simple equality tests: |
| 24 | + |
| 25 | +(defexpect equality |
| 26 | + (expect 1 (* 1 1)) |
| 27 | + (expect "foo" (str "f" "oo"))) |
| 28 | + |
| 29 | +;; the expected outcome can be a regular expression: |
| 30 | + |
| 31 | +(defexpect regex-1 |
| 32 | + (expect #"foo" "It's foobar!")) |
| 33 | + |
| 34 | +;; since that has only a single expectation, it can be written more succinctly: |
| 35 | + |
| 36 | +(defexpect regex-2 #"foo" "It's foobar!") |
| 37 | + |
| 38 | +;; the expected outcome can be an exception type: |
| 39 | + |
| 40 | +(defexpect divide-by-zero ArithmeticException (/ 12 0)) |
| 41 | + |
| 42 | +;; the expected outcome can be a predicate: |
| 43 | + |
| 44 | +(defexpect no-elements empty? (list)) |
| 45 | + |
| 46 | +;; the expected outcome can be a type: |
| 47 | + |
| 48 | +(defexpect named String (name :foo)) |
| 49 | + |
| 50 | +;; if the actual value is a collection, the expected outcome can be an element or subset "in" that collection: |
| 51 | + |
| 52 | +(defexpect collections |
| 53 | + (expect {:foo 1} (in {:foo 1 :cat 4})) |
| 54 | + (expect :foo (in #{:foo :bar})) |
| 55 | + (expect :foo (in [:bar :foo]))) |
| 56 | +``` |
| 57 | + |
| 58 | +Just like `deftest`, the `defexpect` macro creates a function that contains the test\(s\). You can run each function individually: |
| 59 | + |
| 60 | +```clojure |
| 61 | +user=> (equality) |
| 62 | +nil |
| 63 | +``` |
| 64 | + |
| 65 | +If the test passes, nothing is printed, and `nil` is returned. Let's look at a failing test: |
| 66 | + |
| 67 | +```clojure |
| 68 | +user=> (defexpect inequality (* 2 21) (+ 13 13 13)) |
| 69 | +#'user/inequality |
| 70 | +user=> (inequality) |
| 71 | + |
| 72 | +FAIL in (inequality) (.../README.md:67) |
| 73 | +expected: (=? (* 2 21) (+ 13 13 13)) |
| 74 | + actual: 39 |
| 75 | +nil |
| 76 | +``` |
| 77 | + |
| 78 | +The output is produced by `clojure.test`'s standard reporting functionality. |
| 79 | +The `=?` operator is an extension to `clojure.test`'s `assert-expr` multimethod |
| 80 | +that allows for Expectations style of predicate-or-equality testing (based on |
| 81 | +whether the "expected" expression resolves to a function or some other value): |
| 82 | + |
| 83 | +```clojure |
| 84 | +user=> (defexpect indivisible odd? (+ 1 1)) |
| 85 | +#'user/indivisible |
| 86 | +user=> (indivisible) |
| 87 | + |
| 88 | +FAIL in (indivisible) (.../README.md:83) |
| 89 | +expected: (=? odd? (+ 1 1)) |
| 90 | + actual: (not (odd? 2)) |
| 91 | +nil |
| 92 | +``` |
| 93 | + |
| 94 | +Here we see the predicate (`odd?`) being applied in the "actual" result from |
| 95 | +`clojure.test`. |
| 96 | + |
| 97 | +`expectations.clojure.test` supports the following features from Expectations so far: |
| 98 | +* simple equality test |
| 99 | +* simple predicate test |
| 100 | +* class test -- see `named` above |
| 101 | +* exception test -- see `divide-by-zero` above |
| 102 | +* regex test -- see `regex-1` and `regex-2` above |
| 103 | +* `(expect expected-expr (from-each [a values] (actual-expr a)))` |
| 104 | +* `(expect expected-expr (in actual-expr))` -- see `collections` above |
| 105 | +* `(expect (more-of destructuring e1 a1 e2 a2 ...) actual-expr)` |
| 106 | +* `(expect (more-> e1 a1 e2 a2 ...) actual-expr)` -- where `actual-expr` is threaded into each `a1`, `a2`, ... expression |
| 107 | +* `(expect (more e1 e2 ...) actual-expr)` |
| 108 | +* `(expect expected-expr (side-effects [fn1 fn2 ...] actual-expr))` |
| 109 | + |
| 110 | +Read [the Expectations documentation](https://clojure-expectations.github.io/) |
| 111 | +for more details of these features. |
| 112 | + |
| 113 | +Tests defined with `defexpect` behave just like tests defined with `deftest` and |
| 114 | +all of the existing `clojure.test`-based tooling will work with them, including |
| 115 | +fixtures, test runners, and other libraries that patch `clojure.test` to improve |
| 116 | +its error reporting and other features. |
| 117 | + |
| 118 | +## Why? |
| 119 | + |
| 120 | +Given the streamlined simplicity of Expectations, you might wonder why you |
| 121 | +would want to migrate your Expectations test suite to `clojure.test`-style |
| 122 | +named tests? The short answer is **tooling**! Whilst Expectations has |
| 123 | +well-maintained, stable plugins for Leiningen and Boot, as well as an Emacs mode, |
| 124 | +the reality is that Clojure tooling is constantly evolving and most of those |
| 125 | +tools -- such as the excellent [https://cider.readthedocs.io/en/latest/](CIDER), |
| 126 | +[https://cursive-ide.com/](Cursive), |
| 127 | +and the more recent [https://atom.io/packages/proto-repl](ProtoREPL) |
| 128 | +and [Chlorine](https://atom.io/packages/chlorine) (both for Atom) -- |
| 129 | +are going to focus on Clojure's built-in testing library first. |
| 130 | +Support for the original form of Expectations, using unnamed tests, is |
| 131 | +non-existent in Cursive, and can be problematic in other editors. |
| 132 | + |
| 133 | +A whole ecosystem |
| 134 | +of tooling has grown up around `clojure.test` and to take advantage of |
| 135 | +that with Expectations, we either need to develop compatible extensions to each |
| 136 | +and every tool or we need Expectations to be compatible with `clojure.test`. |
| 137 | + |
| 138 | +One of the big obstacles for that compatibility is that, by default, Expectations |
| 139 | +generates "random" function names for test code (the function names are based on the |
| 140 | +hashcode of the text form of the `expect` body), which means the test |
| 141 | +name changes whenever the text of the test changes. To address that, the new |
| 142 | +`expectations.clojure.test` namespace introduces named expectations via |
| 143 | +the `defexpect` macro (mimicking `clojure.test`'s `deftest` |
| 144 | +macro). Whilst this goes against the [https://clojure-expectations.github.io/odds-ends.html](Test Names |
| 145 | +philosophy) that Expectations was created with, it buys us a lot in terms of |
| 146 | +tooling support! |
| 147 | + |
5 | 148 | ## Test & Development
|
6 | 149 |
|
7 | 150 | To test, run `clj -A:test:runner`.
|
8 | 151 |
|
| 152 | +## TODO |
| 153 | + |
| 154 | +Add tests(!) and more examples. |
| 155 | + |
9 | 156 | ## License & Copyright
|
10 | 157 |
|
11 | 158 | Copyright © 2018-2019 Sean Corfield, all rights reserved.
|
|
0 commit comments