clojure.test helpers
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
scripts
src/com/grzm/tespresso
test
.dir-locals.el
.gitignore
LICENSE
README.markdown
build.boot
deps.edn
project.clj

README.markdown

tespresso

Test with a little caffeine.

Tespresso is a collection of clojure.test helpers.

Releases and Dependency Information

Releases are on Clojars.

Clojure CLI/deps.edn coordinates:

{com.grzm/tespresso.alpha {:mvn/version "0.1.11"}}

Leiningen/Boot dependency information:

[com.grzm/tespresso.alpha "0.1.11"]

Maven dependency information:

<dependency>
  <groupId>com.grzm</groupId>
  <artifactId>tespresso.alpha</artifactId>
  <version>0.1.11</version>
</dependency>

Usage

Clojure Spec helpers

Tespresso provides a defcheck macro for testing as well as a check? assert-expr to exercise individual specs.

(ns com.example.spec-test
  (:require
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as stest]
   [clojure.test :refer [deftest is]]
   [com.grzm.tespresso.spec.alpha :refer [defcheck]))

(def stc-opts-key #?(:clj :clojure.spec.test.check/opts
                     :cljs :cljs.spec.test.check/opts))

(defn adder [a b]
  (+ a b))

(s/fdef adder
        :args (s/cat :a int? :b int?)
        :ret int?)

(deftest check-adder
  (is (com.grzm.tespresso.spec/check?
        (stest/check `adder {stc-opts-key {:num-tests 100}}))))

The defcheck macro provides a convenient shorthand for this simple case.

(defcheck spec-test-adder `adder)

defcheck is currently Clojure only.

Test ex-data

Clojure's ex-info is great. Confirm you're getting back what you expect when an exception is thrown.

(ns com.example.thrown-test
  (:require
   [clojure.test :refer [deftest is]]
   [com.grzm.tespresso.alpha :as tespresso]))

(deftest has-expected-ex-data
  (is (com.grzm.tespresso/thrown-with-data?
        #(= ::self-inflicted (:cause %))
        (throw (ex-info "yeah, I'm gonna throw"
                        {:cause ::self-inflicted,
                         :a-key :a-value
                         :another-key :another-value})))))

Helper functions tespresso/ex-data= and tespresso/ex-data-select= provide convenient short-hands for matching all or partial key-values in ex-data.

;; match all keys and values
(deftest has-expected-ex-data-full-match
  (is (com.grzm.tespresso/thrown-with-data?
        (tespresso/ex-data= {:cause ::self-inflicted,
                             :a-key :a-value,
                             :another-key :another-value})
        (throw (ex-info "Still throwin'"
                        {:cause ::self-inflicted,
                         :a-key :a-value
                         :another-key :another-value})))))

;; match just the key-vals provided
(deftest has-expected-ex-data-select-match
  (is (com.grzm.tespresso/thrown-with-data?
        (tespresso/ex-data-select= {:cause ::self-inflicted,
                                    :a-key :a-value})
        (throw (ex-info "Can't stop me now!"
                        {:cause ::self-inflicted,
                         :a-key :a-value
                         :another-key :another-value})))))

An optional regex can be passed to also match the message.

;; match just the key-vals provided
(deftest has-expected-ex-data-select-match
  (is (com.grzm.tespresso/thrown-with-data?
        #"^Can't stop"
        (tespresso/ex-data-select= {:cause ::self-inflicted,
                                    :a-key :a-value})
        (throw (ex-info "Can't stop me now!"
                        {:cause ::self-inflicted,
                         :a-key :a-value
                         :another-key :another-value})))))

Test Component systems

The with-system and with-system-options macros help testing systems built with Stuart Sierra's component library.

The with-system macro takes an initial value of a system, starts it, and makes it available as the value of an atom. After evaluating the body, the system is stopped.

(require
  '[com.grzm.tespresso.component.alpha :refer [with-system])

(with-system [sys init-sys-value]
   ;; access system value by deferencing `sys` atom
   (is (= [:app :db] (keys @sys))))

Test clojure.tools.logging

Sometimes you might want to confirm logging is working as expected. Test clojure.tools.logging using the with-logging macro to capture log output. Available for Clojure only.

(ns com.example.tools-logging-test
  (:require
   [clojure.test :refer [deftest is]]
   [clojure.tools.logging :as log]
   [com.grzm.tespresso.tools-logging.alpha :refer [with-logging]]))

(deftest test-logging
  (with-logging [#{:info :warn} log-entries]
    (log/tracef "Some trace message")
    (log/infof "Some info message")
    (log/warnf "Some example warning")
    (is (= [["com.example.tools-logging-test" :info nil "Some info message"]
            ["com.example.tools-logging-test" :warn nil "Some example warning"]]
           @log-entries))))

Compare bytes

In Clojure, com.grzm.tespresso.bytes relies on Zach Tellman's byte-streams library.

(ns com.example.bytes-test
  (:require
   [clojure.test :refer [deftest is]]
   [com.grzm.tespresso.alpha :as tespresso :refer [capture-test-var]]
   [com.grzm.tespresso.bytes.alpha :as bytes]))

(deftest byte-inequality-example
  (is (com.grzm.tespresso.bytes/bytes=
        (bytes/byte-buffer [0 1 (int \a) 3])
        (bytes/byte-buffer [0 2 4 3]))))

Test clojure.test?

When developing new assertions and test macros, it's helpful to actually test the test output, particularly when you want to confirm a failing test reports correctly. Relying on visual inspection of test run output is problematic, not only because you might miss details, but also because a test that's failing unexpectedly may get lost in the expected failing output.

(ns com.example.test-test-output
  (:require
   [clojure.test :refer [deftest is]]
   [com.grzm.tespresso.alpha :as tespresso :refer [capture-test-var]))

(deftest ^::capture test-to-capture
  (is (= 1 2)))

(deftest test-capture
  (let [{:keys [test-out] :as capture} (capture-test-var #'test-to-capture)]
    ;; com.grzm.tespresso/lines-match? takes an sequence of
    ;; regex patterns or strings to match against the output
    (is (com.grzm.tespresso/lines-match?
          [#"FAIL in \(test-to-capture\)"
           "expected: (= 1 2)"
           "  actual: (not (= 1 2))"]
           test-out))))

;; implement test-ns-hook to ignore marked tests during a test run
(defn test-ns-hook
  []
  (tespresso/test-ns-interns-sans-meta-key
    (ns-interns 'com.example.test-test-output) ::capture))

capture-test-var returns a map of :test-out, :out, :report-counters, and :reports.

Note that while test-ns-hook is supported by clojure.test (and cljs.test), not all test runners support test-ns-hook. The test-ns-interns-sans-meta-key

Tespresso eats its own dog food

For more examples of Tespresso in action, take a look at Tespresso's own tests.

Testing Tespresso

Testing Clojure implementation

lein test

Testing JVM ClojureScript implementation

lein clean && lein cljsbuild once

Testing self-hosted ClojureScript implementation

scripts/test-self-host

Not all functionality is tested under self-hosted environments.

License

© 2017-2018 Michael Glaesemann

Released under the MIT License. See LICENSE for details.