(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"
;=> nilTests 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)))))