(ns my.namespace
(:require [clojure.test
:refer [deftest is run-tests]]))
(deftest my-test
(prn "My test ran")
(is (= 1 1)))
(my-test)
;;; "My test ran"
;=> nil
The problem is not that testing is the bottleneck. The problem is that you don’t know what’s in the bottle.
(ns my.namespace
(:require [clojure.test
:refer [deftest is run-tests]]))
(deftest my-test
(prn "My test ran")
(is (= 1 1)))
(my-test)
;;; "My test ran"
;=> nil
Tests are functions with no input arguments
Tests make assertions: (is (= 1 1))
(deftest my-failing-test
(prn "My failing test ran")
(is (= 1 2)))
(my-failing-test)
;;; "My failing test ran"
;;; FAIL in (my-failing-test)
Can define tests in any namespace
Convention: test
dir mirrors src
dir; append test
|
|
The namespace training.a-test contains tests for functions from training.a |
Common to refer all symbols from clojure.test
for convenience:
(ns training.my-namespace-test (:require [clojure.test :refer :all])) (deftest ...)
vs
(ns training.my-namespace-test (:require [clojure.test :as test])) (test/deftest ...)
(run-tests)
;;; "My test ran"
;;; "My failing test ran"
;;; Ran 0 tests containing 0 assertions.
;;; 0 failures, 0 errors.
;=> {:test 1, :pass 0, :fail 0, :error 0, :type :summary}
Runs all tests in the current namespace
(run-tests 'training.my-namespace 'training.other-namespace)
$ lein test
"My test ran" "My failing test ran" Ran 0 tests containing 0 assertions. 0 failures, 0 errors.
Runs all tests in a project
Reloads code and runs tests when you save a file
Leiningen plugin
Add lein-test-refresh to your ~/.lein/profiles.clj
:
{:user {:plugins [[com.jakemccrary/lein-test-refresh "0.22.0"]]}}
Alternatively as a project.clj
dependency:
(defproject sample :dependencies [[org.clojure/clojure "1.8.0"]] :profiles {:dev {:plugins [[com.jakemccrary/lein-test-refresh "0.22.0"]]}})
$ lein test-refresh
Watches for changes from the command line
Change my-test
to print a new message
Tests are re-run as soon as you save the file
(deftest my-test (prn "My test ran immediately"))
Use lein-test-refresh like a REPL
Test more
(deftest inc-adds-one-test (is (= 2 (inc 1))))
=> Ran 1 tests containing 1 assertions. 0 failures, 0 errors.
(= expected actual)
Expected: value literal
Actual: result of invoking the function under test
(deftest broken-test (is (= 1 (inc 1))))
=> FAIL in (broken-test) expected: (= 1 (inc 1)) actual: (not (= 1 2))
(deftest odd-test (is (odd? 1)))
(deftest create-test (is (create-thing)))
(deftest pythag-test (is (= (* 5 5) (+ (* 3 3) (* 4 4))) "The square of the hypotenuse is equal to the sum of the squares of the other two sides"))
expected: (= {:foo :bar, :baz :quux} {:foo :bar, :baz :quux} {:fo :bar, :baz :quux}) actual: (not (= {:foo :bar, :baz :quux} {:foo :bar, :baz :quux} {:fo :bar, :baz :quux}))
Huh?
expected: {:foo :bar, :baz :quux} actual: {:fo :bar, :baz :quux} diff: - {:foo :bar} + {:fo :bar}
pjstadig/humane-test-output
(or venantius/ultra
)
~/.lein/profiles.clj
:
{:user {:dependencies [[pjstadig/humane-test-output "0.8.3"]] :injections [(require 'pjstadig.humane-test-output) (pjstadig.humane-test-output/activate!)]}}
(deftest math-test (testing "Basic math" (is (odd? 1)) (is (= 2 (inc 1)))) (testing "Pythagoras" (is (= (* 5 5) (+ (* 3 3) (* 4 4))) "The square of the hypotenuse is equal to the sum of the squares of the other two sides"))
(are [x y] (= x y) 2 (+ 1 1) 4 (* 2 2))
Concisely expresses multiple assertions
Disadvantages |
Easy to make an error in the syntax
Overly terse
Line numbers are not preserved (harder to find the failing test)
(defn maybe-inc [x] (if (= 42 x) (throw (ex-info "oh no" {})) (inc x)))
(deftest test-maybe-inc-throws (is (thrown? Exception (maybe-inc 42))) (is (thrown-with-msg? Exception #"oh no" (maybe-inc 42))))
(use-fixtures :each (fn print-enter-exit [tests] (println "before") (tests) (println "after")))
A fixture is just a function
Takes a test and calls it (tests are functions)
Set up and tear down resources (database connections etc)
:each
means run for every test in the namespace
(use-fixtures :once (fn capture-prints [f] (with-out-str (f))))
This fixture captures output, prevents clutter
:once
per namespace
Common use case is when doing database tests
Wrap the test execution inside a transaction
Rollback after the test completes
Avoids the need to clean up data
(defn post [url] {:body (str "Hello world")})
(deftest test-post (with-redefs [str (fn [& args] "Goodbye world")] (is (= {:body "Goodbye world"} (post "http://service.com/greet")))))
let does not suffice, str is outside of scope |
Replace any var using with-redefs
Disable dependencies during the test
Isolate particular behaviors
Test exceptional conditions
always throw
never throw
Print out an intermediary values
(defn shazam [a b] (/ 1 (+ a b) (+ a (* a b))))
What is (+ a (* a b))
evaluating to? (doto … (prn))
(defn shazam [a b] (/ 1 (+ a b) (doto (+ a (* a b)) (prn "***"))))
(shazam 1 2) => 3 "***" 1/9
Also useful for Java interop:
(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)) => {"a" 1, "b" 2}
We get the constructed object, with side-effects applied
Ask the REPL questions
Build small incremental functions
Write tests
See manual end of section 4
(defn pythag [a b] (Math/sqrt (+ (* a a) (* b b))))
(deftest test-pythag (is (= 5 (pythag 4 3))) (is (= 13 (pythag 12 5))))
(defn post [url] {:body (str "Hello world")})
(deftest test-post (let [c (atom 0)] (with-redefs [str (fn [& args] (swap! c inc) "Goodbye world")] (post "http://service.com/greet") (post "http://service.com/greet") (post "http://service.com/greet") (is (= 3 @c)))))