Testování v Clojure
Ačkoliv Rich Hickey říká, že nepíše unit testy, přece jenom do Clojure zahrnul “framework” pro unit testy. Já sám, naopak, jsem již léty TDD infikován a testy píšu rád. Takže, jak se to dělá v Clojure? Nejprve natáhneme knihovnu clojure.test
:
(ns my-tests (:use clojure.test))
; nil
Aserce (tvrzení)
Základem testů je makro is
:
(is (= 2 (+ 1 1)))
; true
(is (odd? 1))
; true
(is (even? 1) "truth about one")
;
; FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:30)
; truth about one
; expected: (even? 1)
; actual: (not (even? 1))
; false
Více assertions/parametrů se dá testovat pomocí makra are
:
(are [x y] (= 42 (+ x y))
21 21
20 22
42 0)
; true
Vyhození výjimky se dá otestovat pomocí funkce thrown?
:
(is (thrown? ArithmeticException (/ 42 0)))
; #<ArithmeticException java.lang.ArithmeticException: Divide by zero>
(is (thrown? ArithmeticException (/ 42 42)))
;
; FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:48)
; expected: (thrown? ArithmeticException (/ 42 42))
; actual: nil
; nil
Definice testů
OK, to byly tvrzení (assertions). Jak definuji test? Jedna z možností je makro with-test
:
(with-test
(defn add [x y]
(+ x y))
(is (= 42 (add 21 21)))
(is (= 42 (add 20 22)))
(is (= 42 (add 42 0))))
Druhou možností je použít makro deftest
. Jeho výhodou je, že vytváří funkci (v našem případě ultimate-addition
), kterou lze zavolat z libovolného namespace. To umožňuje mít testy v samostatném souboru tak, jak jsme z unit testů zvyklí.
(deftest ultimate-addition
(is (= 42 (add 21 21)))
(is (= 42 (add 20 22)))
(is (= 42 (add 42 0))))
Spuštění testů
Máme definované dva testy (a šest asercí). Jak je spustíme? Testy v aktuálním namespace odpálíme funkcí run-tests
:
(run-tests)
;
; Testing my-tests
;
; Ran 2 tests containing 6 assertions.
; 0 failures, 0 errors.
; {:type :summary, :pass 6, :test 2, :error 0, :fail 0}
Testy v jiném namespace spustíme obdobně:
(run-tests 'some.namespace 'some.other.namespace)
Automatizace testů
Tak. Konec srandiček! Velký kluci buildujou (a spouští testy) Leiningenem. Prvně vytvoříme projekt příkazem lein new lein-tests
:
Test first! Napíšeme testy (soubor core.clj
v adresáři test
):
(ns lein-tests.test.core
(:use [lein-tests.core])
(:use [clojure.test]))
(deftest split-line-test
(is (= ["foo" "bar"]
(split-line "foo;bar" #";")))
(is (= ["foo" "bar"]
(split-line "foo,bar" #", ?")))
(is (= ["foo" "bar"]
(split-line "foo, bar" #", ?"))))
(deftest get-phone-test
(is (= "123456789"
(get-phone "123456789;Guido")))
(is (= ""
(get-phone ";Guido"))))
A napíšeme (testované) funkce (soubor core.clj
v adresáři src
):
(ns lein-tests.core
(:use [clojure.string :only (split)]))
(defn split-line [line separator]
(split line separator))
(defn get-phone [line]
(first (split-line line #";")))
No a na závěr testy spustíme příkazem lein test
. Nádhera!