Obsah
1. Programovací jazyk Clojure – testování s využitím knihovny Expectations
2. Vytvoření nového projektu a úprava projektového souboru
3. Testy napsané s využitím knihovny clojure.test
4. Testy napsané s využitím knihovny Expectations
5. Porovnání výsledků běhu obou testů
6. Další možnosti makra expect
7. Regulární výrazy v makru expect
9. Kontrola typu a test, zda byla vyvolána výjimka
10. Repositář s dnešními demonstračními příklady
11. Odkazy na předchozí části tohoto seriálu
1. Programovací jazyk Clojure – testování s využitím knihovny Expectations
V článku Leiningen: nástroj pro správu projektů napsaných v Clojure (3) jsme se mj. zabývali i způsobem tvorby jednotkových testů (unit tests) a jejich následném spouštění s využitím nástroje Leiningen. Popsány byly základní možnosti knihovny clojure.test, a to zejména z toho důvodu, že se jedná o knihovnu, která je standardní součástí každé instalace jazyka Clojure (ostatně jejím autorem je sám Rich Hickey) a navíc se jedná o knihovnu, která je poměrně nízkoúrovňová a autorům testů tak přináší relativně málo různých nechtěných „překvapení“ (jinými slovy: nesnaží se být chytřejší než programátor). Ovšem právě ona zmiňovaná nízkoúrovňovost na druhou stranu znamená, že testy mohou být poměrně nečitelné, objevuje se v nich relativně velké množství opakujícího se kódu a ani způsob hlášení chyb v testovaném kódu není dokonalý, protože chybová hlášení obsahují jen informaci o tom, že skutečně vrácená hodnota se odlišuje od hodnoty očekávané a pokud celý test vyhodí výjimku, vypíše se pouze špatně čitelný výpis obsahu zásobníkových rámců (stack trace).
Poněkud odlišným směrem se vydal vývojář Jay Fields, který naprogramoval nástroj nazvaný Expectations (resp. expectations, tedy s malým „e“ na začátku názvu). Tento nástroj sice interně využívá výše zmíněnou knihovnu clojure.test, ovšem staví nad ní mezivrstvu zajišťující rozhraní pro psaní přehlednějších jednotkových testů. V této mezivrstvě nalezneme „inteligentní“ makro expect, které samo o sobě postačuje pro napsání většiny testů. Pro další zjednodušení jsou v nástroji Expectations dostupná i další makra, především pak makro more->, more-of a v neposlední řadě taktéž from-each. To však není vše, protože podobně inteligentně zpracovaná jsou i hlášení o chybách, která jsou generovaná při spouštění jednotkových testů. Namísto obvyklé strohé informace o tom, že se například vypočtená kolekce odlišuje od kolekce očekávané, dokáže knihovna Expectations vypsat i další informace, například tehdy, když vrácená kolekce obsahuje jen další prvky (a zbytek kolekce se shoduje s kolekcí očekávanou), některé prvky chybí či se liší pořadí prvků. I v případě vzniku výjimky se namísto celého obsahu zásobníkových rámců vypíšou pouze relevantní informace.
2. Vytvoření nového projektu a úprava projektového souboru
Před podrobnějším popisem možností knihovny Expectations se podívejme na praktický příklad, v němž opět využijeme nástroj Leiningen. Jedná se o nám již známý a taktéž popsaný projekt obsahující funkci pro výpočet faktoriálu. Samotná funkce pro výpočet faktoriálu je otestovaná několika jednotkovými testy používajícími knihovnu clojure.test. Zkusme si nyní tento projekt rozšířit takovým způsobem, aby bylo možné použít vlastnosti nabízené knihovnou Expectations. Nový projekt vytvoříme známým příkazem:
lein new app factorial3 Generating a project called factorial2 based on the 'app' template.
Následně je nutné upravit soubor project.clj obsahující mj. i všechny knihovny a pluginy, které jsou projektem používané. V našem případě přidáme jednu knihovnu a jeden plugin. V následujícím výpisu jsou změněné části projektového souboru označeny tučným písmem:
(defproject factorial3 "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [expectations "2.0.9"]] :main ^:skip-aot factorial2.core :target-path "target/%s" :plugins [[lein-expectations "0.0.8"] [lein-cloverage "1.0.2"]] :profiles {:uberjar {:aot :all}})
Struktura projektu po jeho vytvoření:
├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── factorial3 │ └── core.clj └── test └── factorial3 └── core_test.clj 6 directories, 6 files
Zdrojový kód src/factorial3/core.clj:
(ns factorial3.core (:gen-class)) (defn factorial [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn factorial2 [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn factorial3 [n] (if (neg? n) (throw (IllegalArgumentException. "negative numbers are not supported!")) (apply * (range 1 (inc n))))) (defn -main "I don't do a whole lot ... yet." [& args] (doseq [i (range 0 10)] (println i "! = " (factorial i))))
Instalaci všech závislých knihoven zajistí příkaz:
lein deps Retrieving lein-expectations/lein-expectations/0.0.8/lein-expectations-0.0.8.pom from clojars Retrieving expectations/expectations/1.4.5/expectations-1.4.5.pom from clojars Retrieving org/clojure/clojure/1.3.0/clojure-1.3.0.jar from central Retrieving lein-expectations/lein-expectations/0.0.8/lein-expectations-0.0.8.jar from clojars Retrieving expectations/expectations/1.4.5/expectations-1.4.5.jar from clojars Retrieving expectations/expectations/2.0.9/expectations-2.0.9.pom from clojars Retrieving junit/junit/4.8.1/junit-4.8.1.pom from central Retrieving junit/junit/4.8.1/junit-4.8.1.jar from central Retrieving expectations/expectations/2.0.9/expectations-2.0.9.jar from clojars
Leiningen nyní umožňuje spouštět novou sadu testů příkazem:
lein expectations Ran 0 tests containing 0 assertions in 8 msecs 0 failures, 0 errors.
Ve skutečnosti využívá knihovna Expectations řídicí kódy terminálu pro obarvení výstupu, takže výsledek může vypadat takto:
3. Testy napsané s využitím knihovny clojure.test
Zopakujme si nejdříve, jakým způsobem se vytváří jednotkové testy při použití knihovny clojure.test. V tomto případě je skupina testů sdružena deklarací deftest, uvnitř které lze použít makro testing zajišťující další úroveň sdružování, není to však nutné (já zde toto makro používám pro zvýšení čitelnosti). V těle makra testing jsou umístěny funkce a makra is a are pro zápis volání funkcí (či metod) a očekávaného výsledku. Navíc je možné přes thrown? otestovat, zda volaná funkce či metoda vyhodila výjimku správného typu. Testy pro zjištění korektní funkcionality funkce factorial mohou vypadat následovně. Za povšimnutí stojí i způsob použití makra ns na začátku testu, především pak třetí řádek, který zajistí nahrání všech testovaných funkcí (to se neprovádí automaticky):
(ns factorial3.core-test (:require [clojure.test :refer :all] [factorial3.core :refer :all])) (deftest factorial-test (testing "Factorial" (is ( = (factorial 0) 1) "beginning") (is ( = (factorial 1) 1) "beginning") (is ( = (factorial 2) (* 1 2)) "still easy") (is ( = (factorial 5) (* 1 2 3 4 5)) "5!") (is ( = (factorial 6) 720) "6!"))) (deftest negative-factorial-test (testing "Negative tests" (is ( = (factorial 0) 0) "negative test case #1") (is ( = (factorial 1) 0) "negative test case #2") (is ( = (factorial 2) 0) "negative test case #3"))) (deftest exception-test (testing "If factorial throws exception" (is (thrown? IllegalArgumentException (factorial -1))) (is (thrown? IllegalArgumentException (factorial -2))) (is (thrown? IllegalArgumentException (factorial -100))))) (deftest negative-exception-test (testing "(negative test) If factorial throws exception" (is (thrown? IllegalArgumentException (factorial 1))) (is (thrown? IllegalArgumentException (factorial 2))) (is (thrown? IllegalArgumentException (factorial 3)))))
Očekávané výsledky:
- Test factorial-test by měl být spuštěn bez chyby (vypočtené výsledky odpovídají očekávaným hodnotám).
- Test negative-factorial-test by měl nahlásit tři chyby (testovaná funkce je sice korektní, my však očekáváme odlišné výsledky).
- Test exception-test by měl být spuštěn bez chyby (všechny výjimky jsou skutečně vyhozeny).
- Test negative-exception-test by měl nahlásit tři chyby (výjimky nejsou vyhozeny, protože vstupní parametry 1, 2 a 3 jsou zcela korektní).
4. Testy napsané s využitím knihovny Expectations
Při použití knihovny Expectations se testy zapisují nepatrně odlišným způsobem. Především se zde nepoužívá sdružování s využitím deftest a testing, protože autor knihovny Expectations (Jay Fields) se drží zásady „One assertion per test“. Dále se namísto maker is a are, které většinou vyžadují explicitní zápis porovnání, používá inteligentní makro nazvané expect. Toto makro očekává dva parametry. Prvním parametrem je očekávaná hodnota, druhým parametrem je pak většinou volání nějaké funkce či jiného makra. expect na základě typu prvního parametru automaticky rozpozná, jakým způsobem se má provádět porovnávání; porovnávat lze totiž jak návratové hodnoty (jakéhokoli typu), tak i například zjistit, zda byla vyhozena očekávaná výjimka, zda má návratová hodnota očekávaný typ atd. atd. My nejdříve použijeme test s porovnáváním návratových hodnot a zjišťováním, zda byla vyhozena očekávaná výjimka. V obou případech je použit stejný formát volání makra expect:
(ns factorial2.core-expect-test (:require [factorial2.core :refer :all]) (:use expectations)) (expect 1 (factorial 0)) (expect 1 (factorial 1)) (expect (* 1 2) (factorial 2)) (expect (* 1 2 3 4 5) (factorial 5)) (expect 720 (factorial 6)) (expect 0 (factorial 0)) (expect 0 (factorial 1)) (expect 0 (factorial 2)) (expect IllegalArgumentException (factorial -1)) (expect IllegalArgumentException (factorial -2)) (expect IllegalArgumentException (factorial -100)) (expect IllegalArgumentException (factorial 1)) (expect IllegalArgumentException (factorial 2)) (expect IllegalArgumentException (factorial 3))
5. Porovnání výsledků běhu obou testů
Podívejme se nyní na způsob prezentace výsledků běhu obou testů. Nejprve se zaměříme na výsledky vypsané knihovnou clojure.test:
lein test factorial2.core-test lein test :only factorial2.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:27) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 1)) actual: nil lein test :only factorial2.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:28) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 2)) actual: nil lein test :only factorial2.core-test/negative-exception-test FAIL in (negative-exception-test) (core_test.clj:29) (negative test) If factorial throws exception expected: (thrown? IllegalArgumentException (factorial 3)) actual: nil lein test :only factorial2.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:15) Negative tests negative test case #1 expected: (= (factorial 0) 0) actual: (not (= 1 0)) lein test :only factorial2.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:16) Negative tests negative test case #2 expected: (= (factorial 1) 0) actual: (not (= 1 0)) lein test :only factorial2.core-test/negative-factorial-test FAIL in (negative-factorial-test) (core_test.clj:17) Negative tests negative test case #3 expected: (= (factorial 2) 0) actual: (not (= 2 0)) Ran 4 tests containing 14 assertions. 6 failures, 0 errors.
Vidíme, že testy nejsou volány v tom pořadí, jak jsou zapsány ve zdrojovém kódu, což v některých případech může způsobovat problémy. Dále je pak patrné, že hlášení o chybách není příliš čitelné, protože se očekávané a skutečně získané výsledky zbytečně ztrácí mezi množstvím závorek a opisech původního zdrojového kódu.
Následuje výstup poskytovaný knihovnou Expectations:
failure in (core_expect_test.clj:14) : factorial2.core-expect-test (expect 0 (factorial 0)) expected: 0 was: 1 failure in (core_expect_test.clj:15) : factorial2.core-expect-test (expect 0 (factorial 1)) expected: 0 was: 1 failure in (core_expect_test.clj:16) : factorial2.core-expect-test (expect 0 (factorial 2)) expected: 0 was: 2 failure in (core_expect_test.clj:22) : factorial2.core-expect-test (expect IllegalArgumentException (factorial 1)) (factorial 1) did not throw IllegalArgumentException failure in (core_expect_test.clj:23) : factorial2.core-expect-test (expect IllegalArgumentException (factorial 2)) (factorial 2) did not throw IllegalArgumentException failure in (core_expect_test.clj:24) : factorial2.core-expect-test (expect IllegalArgumentException (factorial 3)) (factorial 3) did not throw IllegalArgumentException Ran 14 tests containing 14 assertions in 71 msecs 6 failures, 0 errors.
Opět platí, že výstup je ve skutečnosti obarven a vypadá zhruba takto:
Takto generovaný výsledek testů je podle mého názoru čitelnější, neboť jsou zobrazeny jen ty skutečně relevantní informace.
6. Další možnosti makra expect
Pro otestování dalších vlastností makra expect si vytvoříme nový projekt:
lein new app expectations-demo Generating a project called expectations-demo based on the 'app' template.
Upravíme projektový soubor project.clj:
(defproject expectations-demo "0.1.0-SNAPSHOT" :description "FIXME: write description" :url "http://example.com/FIXME" :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.6.0"] [expectations "2.0.9"]] :main ^:skip-aot expectations-demo.core :target-path "target/%s" :plugins [[lein-expectations "0.0.8"]] :profiles {:uberjar {:aot :all}})
Struktura projektu po jeho vytvoření:
. ├── doc │ └── intro.md ├── LICENSE ├── project.clj ├── README.md ├── resources ├── src │ └── expectations_demo │ └── core.clj └── test └── expectations_demo └── core_test.clj 6 directories, 6 files
Všechny další úpravy se budou týkat pouze souboru test/expectations_demo/core_test.clj. Další vlastnosti makra expect jsou popsány přímo v tomto souboru formou poznámek:
(ns expectations-demo.core-test (:require [expectations :refer :all] [expectations-demo.core :refer :all])) ; ------------------------------------------------------------ ; Porovnání návratové hodnoty ; ------------------------------------------------------------ (expect 42 (* 6 7)) (expect "Hello world" (str "Hello" " " "world")) (expect 2/3 (/ 2 3)) ; ------------------------------------------------------------ ; Prvním parametrem makra expect může být například i predikát ; ------------------------------------------------------------ (expect even? 2) (expect odd? 3) (expect nil? nil) (expect seq? '(1 2 3)) (expect seq? (seq [1 2 3]))
7. Regulární výrazy v makru expect
Pokud je prvním parametrem regulární výraz (regexp), bude se „matchovat“ s řetězcem, který je očekáván jako druhý parametr (či výsledek volané funkce/makra). Připomeňme si, že regulární výraz lze v jazyku Clojure považovat za samostatný datový typ a nikoli za běžný řetězec (což zjednodušuje zápis regulárních výrazů):
(expect #"Expect" "Expectations") (expect #"^[a-zA-Z]+$" "Hello") (expect #"^[a-zA-Z]+$" "123qwe") ; nebude splněno (expect #"^[a-zA-Z0-9]+$" "123qwe") ; bude splněno (expect #"[\s]*" "123qwe") (expect #"^([A-Z][a-z]+)+$" "CamelCaseString") ; bude splněno (expect #"^([A-Z][a-z]+)+$" "CamelCaseStringS") ; nebude splněno (expect #"^([A-Z][a-z]+)+$" "camel_case_string") ; nebude splněno
Výsledek běhu testů:
failure in (core_test.clj:45) : expectations-demo.core-test (expect #"^[a-zA-Z]+$" "123qwe") regex #"^[a-zA-Z]+$" not found in "123qwe" failure in (core_test.clj:53) : expectations-demo.core-test (expect #"^([A-Z][a-z]+)+$" "CamelCaseStringS") regex #"^([A-Z][a-z]+)+$" not found in "CamelCaseStringS" failure in (core_test.clj:55) : expectations-demo.core-test (expect #"^([A-Z][a-z]+)+$" "camel_case_string") regex #"^([A-Z][a-z]+)+$" not found in "camel_case_string"
8. Kolekce v makru expect
Velmi elegantní je práce s kolekcemi, neboť se automaticky rozpoznávají některé typické odlišnosti dvou kolekcí (očekávané hodnoty a hodnoty vypočtené) – přidání prvku, ubrání prvku, prohození prvků atd. Podívejme se na příklady:
; zjištění existence prvku v kolekci (expect 3 (in [1 2 3])) ; porovnání dvou různých kolekcí (expect [1 2] [3 4]) (expect [1 2] [3 4 5 6]) ; různé typy, stejný obsah - test projde (expect [1 2] '(1 2)) ; expect rozpozná zpřeházené prvky (expect [1 2] [2 1]) (expect [1 2 3] [3 2 1]) ; expect rozpozná přidání prvku (expect [1 2] [1 2 3]) (expect [1 2] [1 2 3 4 5]) ; expect rozpozná i ubrání prvku (expect [1 2 3] [1 2]) (expect [1 2 3 4 5] [1 2]) ; dtto pro mapy - opět se eliminuje výpis zbytečných informací (expect #{:name "Bender" :id 42} #{:name "Bender" :id 42}) (expect #{:name "Bender" :id 42} #{:name "Joe" :id 42}) (expect #{:name "Bender" :id 42} #{:name "Joe" :id 1000}) (expect #{:name "Bender" :id 42} #{:name "Bender" :id 42 :foo :bar}) (expect #{:name "Bender" :id 42} #{:name "Bender"}) (expect #{:name "Bender" :id 42} #{:name "Bender" :not-id 42})
Výsledek běhu testů:
failure in (core_test.clj:67) : expectations-demo.core-test (expect [1 2] [3 4]) expected: [1 2] was: [3 4] in expected, not actual: [1 2] in actual, not expected: [3 4] failure in (core_test.clj:69) : expectations-demo.core-test (expect [1 2] [3 4 5 6]) expected: [1 2] was: [3 4 5 6] in expected, not actual: [1 2] in actual, not expected: [3 4 5 6] actual is larger than expected failure in (core_test.clj:75) : expectations-demo.core-test (expect [1 2] [2 1]) expected: [1 2] was: [2 1] in expected, not actual: [1 2] in actual, not expected: [2 1] lists appear to contain the same items with different ordering failure in (core_test.clj:76) : expectations-demo.core-test (expect [1 2 3] [3 2 1]) expected: [1 2 3] was: [3 2 1] in expected, not actual: [1 nil 3] in actual, not expected: [3 nil 1] lists appear to contain the same items with different ordering failure in (core_test.clj:79) : expectations-demo.core-test (expect [1 2] [1 2 3]) expected: [1 2] was: [1 2 3] in expected, not actual: null in actual, not expected: [nil nil 3] actual is larger than expected failure in (core_test.clj:80) : expectations-demo.core-test (expect [1 2] [1 2 3 4 5]) expected: [1 2] was: [1 2 3 4 5] in expected, not actual: null in actual, not expected: [nil nil 3 4 5] actual is larger than expected failure in (core_test.clj:83) : expectations-demo.core-test (expect [1 2 3] [1 2]) expected: [1 2 3] was: [1 2] in expected, not actual: [nil nil 3] in actual, not expected: null expected is larger than actual failure in (core_test.clj:84) : expectations-demo.core-test (expect [1 2 3 4 5] [1 2]) expected: [1 2 3 4 5] was: [1 2] in expected, not actual: [nil nil 3 4 5] in actual, not expected: null expected is larger than actual failure in (core_test.clj:89) : expectations-demo.core-test (expect #{:name "Bender" :id 42} #{:name "Joe" :id 42}) expected: #{:name "Bender" :id 42} was: #{:name "Joe" :id 42} in expected, not actual: #{"Bender"} in actual, not expected: #{"Joe"} failure in (core_test.clj:91) : expectations-demo.core-test (expect #{:name "Bender" :id 42} #{:name 1000 "Joe" :id}) expected: #{:name "Bender" :id 42} was: #{:name 1000 "Joe" :id} in expected, not actual: #{"Bender" 42} in actual, not expected: #{1000 "Joe"} failure in (core_test.clj:93) : expectations-demo.core-test (expect #{:name "Bender" :id 42} #{:bar :name "Bender" :foo :id 42}) expected: #{:name "Bender" :id 42} was: #{:bar :name "Bender" :foo :id 42} in expected, not actual: null in actual, not expected: #{:bar :foo} failure in (core_test.clj:95) : expectations-demo.core-test (expect #{:name "Bender" :id 42} #{:name "Bender"}) expected: #{:name "Bender" :id 42} was: #{:name "Bender"} in expected, not actual: #{:id 42} in actual, not expected: null failure in (core_test.clj:97) : expectations-demo.core-test (expect #{:name "Bender" :id 42} #{:name "Bender" :not-id 42}) expected: #{:name "Bender" :id 42} was: #{:name "Bender" :not-id 42} in expected, not actual: #{:id} in actual, not expected: #{:not-id}
9. Kontrola typu a test, zda byla vyvolána výjimka
; ------------------------------------------------------------ ; Kontrola typu návratové hodnoty ; ------------------------------------------------------------ (expect String (str "Hello world")) (expect Long (* 6 7)) (expect Long (apply * (range 10))) (expect Double (/ 1.0 3)) ; pozor na použití zlomků (expect clojure.lang.Ratio (/ 1 3))
; ------------------------------------------------------------ ; Kontrola vyhození výjimky ; ------------------------------------------------------------ ; výjimka je vyhozena (expect ArithmeticException (/ 1 0)) ; výjimka není vyhozena (expect IndexOutOfBoundsException (nth [1 2 3] 1)) ; výjimka je vyhozena (expect IndexOutOfBoundsException (nth [1 2 3] -1)) ; je vyhozena odlišná výjimka (expect NullPointerException (nth [1 2 3] -1))
Výsledek běhu všech testů:
10. Repositář s dnešními demonstračními příklady
Všechny dnes zmíněné demonstrační příklady byly, podobně jako v předchozích částech tohoto seriálu, uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/clojure-examples. V tabulce zobrazené pod tímto odstavcem naleznete na zdrojové kódy jednotlivých demonstračních příkladů přímé odkazy:
# | Příklad | Github |
---|---|---|
1 | factorial | https://github.com/tisnik/clojure-examples/tree/master/factorial |
2 | factorial2 | https://github.com/tisnik/clojure-examples/tree/master/factorial2 |
3 | factorial3 | https://github.com/tisnik/clojure-examples/tree/master/factorial3 |
4 | expectations-demo | https://github.com/tisnik/clojure-examples/tree/master/expectations-demo |
11. Odkazy na předchozí části tohoto seriálu
- Leiningen: nástroj pro správu projektů napsaných v Clojure
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (2)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (3)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-3/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (4)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-4/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (5)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-5/ - Leiningen: nástroj pro správu projektů napsaných v Clojure (6)
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-6/ - Programovací jazyk Clojure a databáze (1.část)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/ - Pluginy pro Leiningen
http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi/ - Programovací jazyk Clojure a knihovny pro práci s vektory a maticemi (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-knihovny-pro-praci-s-vektory-a-maticemi-2/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-2/ - Programovací jazyk Clojure: syntéza procedurálních textur s využitím knihovny Clisk (dokončení)
http://www.root.cz/clanky/programovaci-jazyk-clojure-synteza-proceduralnich-textur-s-vyuzitim-knihovny-clisk-dokonceni/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (2)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-2/ - Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure (3)
http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure-3/ - Programovací jazyk Clojure a práce s Gitem
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/ - Programovací jazyk Clojure a práce s Gitem (2)
http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/ - Programovací jazyk Clojure – triky při práci s řetězci
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/ - Programovací jazyk Clojure – triky při práci s kolekcemi
http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/ - Programovací jazyk Clojure – práce s mapami a množinami
http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/ - Programovací jazyk Clojure – základy zpracování XML
http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
12. Odkazy na Internetu
- Expectations: příklady atd.
http://jayfields.com/expectations/ - Expectations na GitHubu
https://github.com/jaycfields/expectations - Lein-expectations na GitHubu
https://github.com/gar3thjon3s/lein-expectations - Testing Clojure With Expectations
https://semaphoreci.com/blog/2014/09/23/testing-clojure-with-expectations.html - Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
https://www.reddit.com/r/Clojure/comments/1viilt/clojure_testing_tddbdd_libraries_clojuretest_vs/ - Testing: One assertion per test
http://blog.jayfields.com/2007/06/testing-one-assertion-per-test.html - Rewriting Your Test Suite in Clojure in 24 hours
http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/ - Clojure doc: zipper
http://clojuredocs.org/clojure.zip/zipper - Clojure doc: parse
http://clojuredocs.org/clojure.xml/parse - Clojure doc: xml-zip
http://clojuredocs.org/clojure.zip/xml-zip - Clojure doc: xml-seq
http://clojuredocs.org/clojure.core/xml-seq - Parsing XML in Clojure
https://github.com/clojuredocs/guides - Clojure Zipper Over Nested Vector
https://vitalyper.wordpress.com/2010/11/23/clojure-zipper-over-nested-vector/ - Understanding Clojure's PersistentVector implementation
http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation - Understanding Clojure's PersistentHashMap (deftwice…)
http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html - Assoc and Clojure's PersistentHashMap: part ii
http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html - Ideal Hashtrees (paper)
http://lampwww.epfl.ch/papers/idealhashtrees.pdf - Clojure home page
http://clojure.org/ - Clojure (downloads)
http://clojure.org/downloads - Clojure Sequences
http://clojure.org/sequences - Clojure Data Structures
http://clojure.org/data_structures - The Structure and Interpretation of Computer Programs: 2.2.1 Representing Sequences
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-15.html#%_sec2.2.1 - The Structure and Interpretation of Computer Programs: 3.3.1 Mutable List Structure
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-22.html#%_sec3.3.1 - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc (rozcestník s dokumentací jazyka Clojure)
http://clojuredocs.org/ - Clojure (na Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (na Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - Třída java.lang.String
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html - Třída java.lang.StringBuffer
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html - Třída java.lang.StringBuilder
http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html - StringBuffer versus String
http://www.javaworld.com/article/2076072/build-ci-sdlc/stringbuffer-versus-string.html - Threading macro (dokumentace k jazyku Clojure)
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/-> - Understanding the Clojure → macro
http://blog.fogus.me/2009/09/04/understanding-the-clojure-macro/ - clojure.inspector
http://clojure.github.io/clojure/clojure.inspector-api.html - The Clojure Toolbox
http://www.clojure-toolbox.com/ - Unit Testing in Clojure
http://nakkaya.com/2009/11/18/unit-testing-in-clojure/ - Testing in Clojure (Part-1: Unit testing)
http://blog.knoldus.com/2014/03/22/testing-in-clojure-part-1-unit-testing/ - API for clojure.test – Clojure v1.6 (stable)
https://clojure.github.io/clojure/clojure.test-api.html - Leiningen: úvodní stránka
http://leiningen.org/ - Leiningen: Git repository
https://github.com/technomancy/leiningen - leiningen-win-installer
http://leiningen-win-installer.djpowell.net/ - Clojure 1: Úvod
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/ - Clojure 2: Symboly, kolekce atd.
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/ - Clojure 3: Funkcionální programování
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-3-cast-funkcionalni-programovani/ - Clojure 4: Kolekce, sekvence a lazy sekvence
http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-4-cast-kolekce-sekvence-a-lazy-sekvence/ - Clojure 5: Sekvence, lazy sekvence a paralelní programy
http://www.root.cz/clanky/clojure-a-bezpecne-aplikace-pro-jvm-sekvence-lazy-sekvence-a-paralelni-programy/ - Clojure 6: Podpora pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/ - Clojure 7: Další funkce pro paralelní programování
http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/ - Clojure 8: Identity, stavy, neměnné hodnoty a reference
http://www.root.cz/clanky/programovaci-jazyk-clojure-8-identity-stavy-nemenne-hodnoty-a-referencni-typy/ - Clojure 9: Validátory, pozorovatelé a kooperace s Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-9-validatory-pozorovatele-a-kooperace-mezi-clojure-a-javou/ - Clojure 10: Kooperace mezi Clojure a Javou
http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/ - Clojure 11: Generátorová notace seznamu/list comprehension
http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/ - Clojure 12: Překlad programů z Clojure do bajtkódu JVM I:
http://www.root.cz/clanky/programovaci-jazyk-clojure-12-preklad-programu-z-clojure-do-bajtkodu-jvm/ - Clojure 13: Překlad programů z Clojure do bajtkódu JVM II:
http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/ - Clojure 14: Základy práce se systémem maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/ - Clojure 15: Tvorba uživatelských maker
http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/ - Clojure 16: Složitější uživatelská makra
http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/ - Clojure 17: Využití standardních maker v praxi
http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/ - Clojure 18: Základní techniky optimalizace aplikací
http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/ - Clojure 19: Vývojová prostředí pro Clojure
http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/ - Clojure 20: Vývojová prostředí pro Clojure (Vimu s REPL)
http://www.root.cz/clanky/programovaci-jazyk-clojure-20-vyvojova-prostredi-pro-clojure-integrace-vimu-s-repl/ - Clojure 21: ClojureScript aneb překlad Clojure do JS
http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/ - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - Transient Data Structureshttp://clojure.org/transients