Programovací jazyk Clojure – některé užitečné triky použitelné (nejenom) v testech

Pavel Tišnovský 10. 9. 2015

Programovací jazyk Clojure je navržen tak, aby vývojáře vedl k tvorbě referenčně transparentních funkcí, tj. funkcí bez vedlejších efektů, které se velmi snadno testují. Ovšem programátoři musí při psaní testů pracovat i s funkcemi s vedlejšími efekty. V těchto případech lze využít některé triky.

Obsah

1. Makro are určené pro zpřehlednění testů

2. Referenčně transparentní funkce a funkce s vedlejšími efekty

3. Jak testovat funkce, které tisknou na standardní či chybový výstup?

4. Makro with-out-str

5. Mockování funkcí volaných v průběhu testování

6. Makro with-redefs

7. Zjištění existence funkcí či proměnných v testovaném jmenném prostoru

8. Predikát clojure.test/function?

9. Repositář s dnešními demonstračními příklady

10. Odkazy na předchozí části tohoto seriálu

11. Odkazy na Internetu

1. Makro are určené pro zpřehlednění testů

V dnešním článku o programovacím jazyku Clojure se seznámíme s některými jednoduchými i složitějšími triky, které lze využít při tvorbě testů. První trik spočívá ve využití makra are, které může v mnoha případech zpřehlednit testy, v nichž se neustále volá nám již známé makro is. Jedná se o velmi jednoduchý trik, nicméně si jeho použití ukažme na demonstračním příkladu. Nejprve vytvoříme kostru tohoto příkladu:

lein new app testing1

Dále upravíme module testing1.core následujícím způsobem:

(ns testing1.core
    (:gen-class))
 
(defn add
    [x y]
    (println "Adding" x "to" y)
    (+ x y))
 
(defn -main
    [& args]
    (println (add 1 2)))

Pokusme se nyní napsat jednoduchý test pro funkci add. Test bude uložen v souboru testing1/test/testing1/core_test.clj, jehož kostra byla automaticky vygenerována nástrojem Leiningen, takže nám zbývá jen dopsání vlastní testovací funkce:

(ns testing1.core-test
    (:require [clojure.test :refer :all]
              [testing1.core :refer :all]))
 
(deftest test-add-1
    (testing "function add"
        (is (= 0 (add 0 0)))
        (is (= 3 (add 1 2)))
        (is (= 5/6 (add 1/2 1/3)))))

Takto zapsaný test je plně funkční, o čemž se lze snadno přesvědčit:

lein test
 
lein test testing1.core-test
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
 
Ran 1 tests containing 3 assertions.
0 failures, 0 errors.

Jedinou vážnější nevýhodou je opakované použití makra is a z toho vyplývající záplavy závorek. Aby se psaní testů zpřehlednilo, lze využít makro are, kterému se předá funkce provádějící porovnání (jen se nezapisuje jméno funkce) a za tímto zápisem pak již většinou seznam obsahující očekávané hodnoty a volání testované funkce:

(ns testing1.core-test
    (:require [clojure.test :refer :all]
              [testing1.core :refer :all]))
 
(deftest test-add-1
    (testing "function add"
        (is (= 0 (add 0 0)))
        (is (= 3 (add 1 2)))
        (is (= 5/6 (add 1/2 1/3)))))
 
(deftest test-add-2
    (testing "function add"
        (are [x y] (= x y)
            0   (add 0 0)
            3   (add 1 2)
            5/6 (add 1/2 1/3))))

Oba dva testy test-add-1 a test-add-2 jsou prakticky ekvivalentní, o čemž se opět můžeme snadno přesvědčit:

lein test
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
 
Ran 2 tests containing 6 assertions.
0 failures, 0 errors.

2. Referenčně transparentní funkce a funkce s vedlejšími efekty

Již v úvodních článcích o programovacím jazyce Clojure jsme si řekli, že tento jazyk patří, společně s klasickým LISPem, Scheme, Haskellem či Erlangem do skupiny (ne vždy nutně čistě) funkcionálních jazyků, tj. programovacích jazyků vycházejících z teorie takzvaného λ-kalkulu, jehož autorem je Alonzo Church (na první návrhy LISPu se dokonce můžeme dívat jako na jeden z formalizovaných způsobů zápisu λ-kalkulu, pro nějž jen tak mimochodem existuje mechanismus vyhodnocování jednotlivých λ výrazů; taktéž se tím vysvětluje přítomnost znaku lambda v logu jazyka Clojure). Ve skutečnosti sice Clojure není čistě funkcionálním jazykem, ovšem v případě, že vývojář bude při tvorbě svých aplikací dodržovat zásady funkcionálního programování, bude pro něj mnohem snadnější vytvářet skutečně výkonné aplikace; ať se to již týká snadnější tvorby bezpečných vícevláknových aplikací či možnosti použití mnohdy velmi užitečné funkce memoize.

Připomeňme si, že v programovacím jazyce Clojure jsou funkce považovány za plnohodnotné datové typy, což znamená, že funkce lze navázat na libovolný symbol (a tím vlastně původně anonymní funkci pojmenovat), funkce lze předávat jako parametry do jiných funkcí a funkce mohou být taktéž návratovou hodnotou jiných funkcí – funkce tedy může vytvořit a vrátit jinou funkci. Clojure taktéž podporuje práci s uzávěry (closure(s)), tj. funkcí svázaných s nějakým symbolem vytvořeným vně funkce. Podpora uzávěrů umožňuje například tvorbu funkcí sdílejících společný kontext (GUI) atd. Ovšem vzhledem k tomu, že – jak již víme – Clojure není čistě funkcionálním jazykem, je možné při vytváření uživatelských funkcí přímo z dané funkce přistupovat k nějakému globálnímu symbolu, přesněji řečeno k symbolu „globálnímu“ v rámci nějakého jmenného prostoru. Taktéž lze vytvářet funkce s vedlejším efektem, které například zapisují data do souborů, mění hodnotu navázanou na globální symboly atd.

Vývojáři by však neměli tyto možnosti nabízené programovacím jazykem Clojure zneužívat, protože tím znemožňují využití některých optimalizačních technik a v neposlední řadě si taktéž komplikují možnost testování takto vytvořených funkcí. Namísto toho se ukazuje být velmi výhodné vytvářet již v perexu zmíněné takzvané referenčně transparentní funkce, což jsou funkce, které nepřistupují k žádným globálním symbolům, nemají žádný vedlejší efekt ani si nepamatují žádný vnitřní stav (příkladem „funkce“ s vnitřním stavem je například Math/random). Referenčně transparentní funkci jsou při jejím volání předány parametry a funkce pouze na základě hodnot předaných parametrů vrátí nějaký výsledek. Tato (pochopitelná) vlastnost má jeden důležitý důsledek – chování referenčně transparentní funkce je nezávislé na stavu aplikace a je taktéž zcela nezávislé na tom, kdy je funkce zavolána.

Pravděpodobně nejjednodušší ukázka funkce, která není referenčně transparentní:

(defn random
    []
    (java.lang.Math/random))

Referenčně transparentní funkce jsou typicky používány ve frameworku Clojure Ring:

(defn handler
    [request]
    (let [params (:params request)
          x      (param->number params "x")
          y      (param->number params "y")
          result (compute-result x y)]
    (-> (response/response (render-html-page x y result))
        (response/content-type "text/html; charset=utf-8"))))

3. Jak testovat funkce, které tisknou na standardní či chybový výstup?

Při tvorbě reálných aplikací v programovacím jazyku Clojure se poměrně často setkáme s následujícím problémem: máme za úkol otevřít nějaký zdroj dat (databázi, soubor…), přečíst z tohoto zdroje data a posléze zdroj dat opět zavřít. Dalším praktickým problémem je nastavení přesnosti a zaokrouhlovacího režimu pro objekty typu BigDecimal, provedení nějaké matematické operace a následně obnovení původní přesnosti a zaokrouhlovacího režimu. Všechny tyto problémy tedy vyžadují, aby se provedla nějaká nastavovací operace, která většinou mění prostředí programu, posléze se provede vlastní výpočet/sekvence příkazů a nakonec se obnoví původní prostředí programu. Takto chápané operace se v programovacím jazyce Clojure poměrně často implementují s využitím maker, jejichž název začíná na with-. Příkladem může být makro with-precision pro dočasnou změnu přesnosti a zaokrouhlovacího režimu pro objekty typu BigDecimal:

user=> (with-precision 1 (/ 1M 3))
0.3M
 
 
user=> (with-precision 2 (/ 1M 3))
0.33M
 
 
user=> (with-precision 10 (/ 1M 3))
0.3333333333M
 
 
user=>

Proč se vlastně o makrech with-* zmiňujeme zrovna v dnešním článku věnovaném tvorbě testů? Určité problémy s testováním totiž nastanou již ve chvíli, kdy testovaná funkce provádí tisk na standardní či na chybový výstup. Již tato „maličkost“ totiž ve skutečnosti porušuje referenční transparentnost (funkce mění stav objektu *out* či *err*), což se vlastně ihned projeví ve chvíli, kdy je v testu nutné ověřit, zda funkce skutečně na standardní/chybový výstup tiskne korektní zprávy. Jedna z možností, jak takové funkce testovat, spočívá v použití makra nazvaného with-out-str, které dokáže „zachytit“ všechna volání funkcí print a println. Řetězce, které by se pomocí těchto funkcí normálně vypsaly na standardní výstup, jsou namísto využity jako návratová hodnota makra:

user=> (with-out-str (print "Hello ") (print "world") (print '!))
"Hello world!"
 
 
user=> (def vystup (with-out-str (print "Hello ") (print "world") (print '!)))
#'user/vystup
 
 
user=> vystup
"Hello world!"
 
 
user=>

Jak již bylo řečeno v úvodním odstavci, pracují makra with- takovým způsobem, že dočasně změní prostředí programu. Nejinak je tomu i v případě makra with-out-str, kde se dočasně změní objekt navázaný na symbol *out*, jenž představuje standardní výstup, neboli v řeči Javy System.out. Symbol *out* je při provádění příkazů předaných makru přesměrován na novou instanci objektu java.io.StringWriter, který je po provedení těla převeden na řetězec s využitím funkce str:

(defmacro with-out-str
  "Evaluates exprs in a context in which *out* is bound to a fresh
  StringWriter.  Returns the string created by any nested printing
  calls."
  {:added "1.0"}
  [& body]
  `(let [s# (new java.io.StringWriter)]
      (binding [*out* s#]
          ~@body
          (str s#))))

4. Makro with-out-str

Doplňme si nyní test pro první demonstrační příklad o zjištění, zda funkce add tiskne na standardní výstup očekávané zprávy. Rozšíření testu není složité, protože pouze namísto návratové hodnoty testujeme text zachycený makrem with-out-str:

(ns testing1.core-test
    (:require [clojure.test :refer :all]
              [testing1.core :refer :all]))
 
(deftest test-add-1
    (testing "function add"
        (is (= 0 (add 0 0)))
        (is (= 3 (add 1 2)))
        (is (= 5/6 (add 1/2 1/3)))))
 
(deftest test-add-2
    (testing "function add"
        (are [x y] (= x y)
            0   (add 0 0)
            3   (add 1 2)
            5/6 (add 1/2 1/3))))
 
(deftest test-add-3
    (testing "function add"
        (is (= "Adding 0 to 0\n"     (with-out-str (add 0 0))))
        (is (= "Adding 1 to 2\n"     (with-out-str (add 1 2))))
        (is (= "Adding 1/2 to 1/3\n" (with-out-str (add 1/2 1/3))))))
 
(deftest test-add-4
    (testing "function add"
        (are [x y] (= x y)
            "Adding 0 to 0\n"     (with-out-str (add 0 0))
            "Adding 1 to 2\n"     (with-out-str (add 1 2))
            "Adding 1/2 to 1/3\n" (with-out-str (add 1/2 1/3)))))

Rozšířený test odzkoušíme:

lein test
 
lein test testing1.core-test
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
 
Ran 4 tests containing 12 assertions.
0 failures, 0 errors.

Poznámka: řádky začínající na „Adding“ byly vypsány v prvních dvou testech test-add-1 a test-add-2, zatímco ve zbývajících testech test-add-3 a test-add-4 byl výstup zachycen a nijak viditelně se při spuštění testů neprojeví.

5. Mockování funkcí volaných v průběhu testování

Podívejme se nyní na nepatrně složitější demonstrační příklad nazvaný testing2, v němž jsou použity funkce pracující s SQL databází SQLite. Projektový soubor tohoto demonstračního příkladu vypadá následovně (povšimněte si především dvou nových prvků uložených ve vektoru :dependencies:

(defproject testing2 "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"]
                 [org.clojure/java.jdbc "0.3.5"]
                 [org.xerial/sqlite-jdbc "3.7.2"]]
  :main ^:skip-aot testing2.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

V hlavním modulu jsou mj. deklarovány funkce pro čtení z databáze i pro zápis do databáze:

(ns testing2.core
    (:gen-class))
 
(require '[clojure.java.jdbc :as jdbc])
 
(def changes-db
    {:classname   "org.sqlite.JDBC"
     :subprotocol "sqlite"
     :subname     "changes.db"
    })
 
(defn read-changes-for-user
    [user-name]
    (jdbc/query changes-db
        [(str "select * from changes where user_name=? order by id;") user-name]))
 
(defn store-changes
    [user-name package description date]
    (jdbc/insert! changes-db :changes
        {:date_time date :user_name user-name :package package :description description}))
 
(defn count-changes-for-user
    [user-name]
    (count (read-changes-for-user user-name)))
 
(defn -main
    [& args]
    (println "Hello, World!"))

Problém může nastat ve chvíli, kdy je zapotřebí otestovat například funkci read-changes-for-user nebo store-changes. Existuje několik způsobů, jak je možné tento problém vyřešit; například se namísto skutečné databáze použije jen její omezená varianta (typicky in-memory databáze naplněná jen nejnutnějšími a předem známými daty). V programovacím jazyce Clojure však existuje ještě jedna možnost – mocking (to je ale slovo!) funkcí jdbc/query a jdbc/insert a jejich náhrada za uživatelsky definované funkce, které se vůči okolnímu kódu chovají jako původní funkce, ale ve skutečnosti provádí jinou činnost, například namísto čtení z databáze vrací konstantu atd. V následující kapitole si ukážeme, jak se tato problematika řeší s využitím makra with-redefs.

6. Makro with-redefs

Testovací modul, který bude (například) testovat korektnost funkcí read-changes-for-user a count-changes-for-user může používat makro nazvané with-redefs. V těle tohoto makra je původní symbol navázán na jinou hodnotu. V našem konkrétním příkladu potřebujeme na symbol jdbc/query (povšimněte si použití jmenného prostoru!) navázat jinou funkci. Původní funkce clojure.java.jdbc/query je volána se dvěma parametry, což bude platit i pro novou uživatelskou funkci:

(with-redefs [jdbc/query (fn [db-spec query] (tělo_nové_anonymní_funkce))]
;
; v těle makra je jdbc/query navázáno na novou anonymní funkci
;
)

Důležité je, že mimo tělo makra with-redefs je na symbol jdbc/query navázána původní funkce pracující s SQL databázemi!

Testovací modul lze napsat například následujícím způsobem. Povšimněte si, že v testu test-read-changes-for-user vrací lokálně deklarovaná anonymní funkce navázaná na symbol jdbc/query druhý prvek druhého parametru (což je předané jméno), zatímco v testu test-count-changes-for-user se vrací konstantní desetiprvkový vektor:

(ns testing2.core-test
  (:require [clojure.test :refer :all]
            [testing2.core :refer :all]))
 
(require '[clojure.java.jdbc :as jdbc])
 
(deftest test-read-changes-for-user
    (testing "read-changes-for-user"
        ; use mock instead of jdbc/query
        (with-redefs [jdbc/query (fn [db-spec query] (second query))]
            (is (= "Pavel" (read-changes-for-user "Pavel"))))))
 
(deftest test-count-changes-for-user
    (testing "read-changes-for-user"
        ; use mock instead of jdbc/query
        (with-redefs [jdbc/query (fn [db-spec query] [1 2 3 4 5 6 7 8 9 10])]
            (is (= 10 (count-changes-for-user "Pavel"))))))

Test si opět odzkoušíme:

lein test
 
lein test testing2.core-test
 
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.

Poznámka: lokálně nahradit je možné prakticky libovolnou funkci z libovolného jmenného prostoru.

Poznámka 2: namísto makra with-redefs je možné použít i přímo funkci with-redefs-fn, která je mimochodem v makru with-redefs volána.

7. Zjištění existence funkcí či proměnných v testovaném jmenném prostoru

V některých případech je nutné v testu zjistit, zda nějaká funkce či proměnná existuje. Pro tento účel se mi vyplatilo deklarovat si pomocnou funkci (přesněji řečeno predikát) nazvanou callable?. Tomuto predikátu se předá libovolný symbol a pokud je tento symbol navázán na skutečnou funkci, vrátí se hodnota true, jinak false:

(defn callable?
    "Test if given function-name is bound to the real function."
    [function-name]
    (clojure.test/function? function-name))

Test na existenci proměnné (či proměnných) lze zajistit predikátem bound?, který se použije následujícím způsobem:

(bound? (find-var 'jméno.modulu/symbol)

8. Predikát clojure.test/function?

Jen stručně se podívejme na použití výše zmíněného predikátu function? při psaní testů. Mějme modul, který se má otestovat:

(ns testing3.core
    (:gen-class))
 
(defn add
    [x y]
    (println "Adding" x "to" y)
    (+ x y))
 
(defn -main
    [& args]
    (println (add 1 2)))

Dva nové testy nazvané test-main-existence a test-add-existence pouze zjišťují existenci funkce -main a add z hlavního modulu, tedy konkrétně ze jmenného prostoru testing3.core:

widgety

(ns testing3.core-test
    (:require [clojure.test :refer :all]
              [testing3.core :refer :all]))
 
(defn callable?
    "Test if given function-name is bound to the real function."
    [function-name]
    (clojure.test/function? function-name))
 
(deftest test-main-existence
    "Check that the testing3.core/-main definition exists."
    (testing "if the testing3.core/-main definition exists."
        (is (callable? 'testing3.core/-main))))
 
(deftest test-add-existence
    "Check that the testing3.core/add definition exists."
    (testing "if the testing3.core/add definition exists."
        (is (callable? 'testing3.core/add))))
 
(deftest test-add-1
    (testing "function add"
        (is (= 0 (add 0 0)))
        (is (= 3 (add 1 2)))
        (is (= 5/6 (add 1/2 1/3)))))

Obligátní zjištění, zda všechny testy proběhnou bez chyb:

lein test
 
Adding 0 to 0
Adding 1 to 2
Adding 1/2 to 1/3
 
Ran 3 tests containing 5 assertions.
0 failures, 0 errors.

9. Repositář s dnešními demonstračními příklady

Všechny tři 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:

10. Odkazy na předchozí části tohoto seriálu

  1. Leiningen: nástroj pro správu projektů napsaných v Clojure
    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 (2)
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-2/
  3. 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/
  4. 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/
  5. 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/
  6. 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/
  7. Programovací jazyk Clojure a databáze (1.část)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-databaze-1-cast/
  8. Pluginy pro Leiningen
    http://www.root.cz/clanky/leiningen-nastroj-pro-spravu-projektu-napsanych-v-clojure-pluginy-pro-leiningen/
  9. 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/
  10. 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/
  11. 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/
  12. 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/
  13. 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/
  14. Seesaw: knihovna pro snadnou tvorbu GUI v jazyce Clojure
    http://www.root.cz/clanky/seesaw-knihovna-pro-snadnou-tvorbu-gui-v-jazyce-clojure/
  15. 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/
  16. 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/
  17. Programovací jazyk Clojure a práce s Gitem
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem/
  18. Programovací jazyk Clojure a práce s Gitem (2)
    http://www.root.cz/clanky/programovaci-jazyk-clojure-a-prace-s-gitem-2/
  19. Programovací jazyk Clojure – triky při práci s řetězci
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-retezci/
  20. Programovací jazyk Clojure – triky při práci s kolekcemi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-triky-pri-praci-s-kolekcemi/
  21. Programovací jazyk Clojure – práce s mapami a množinami
    http://www.root.cz/clanky/programovaci-jazyk-clojure-prace-s-mapami-a-mnozinami/
  22. Programovací jazyk Clojure – základy zpracování XML
    http://www.root.cz/clanky/programovaci-jazyk-clojure-zaklady-zpracovani-xml/
  23. Programovací jazyk Clojure – testování s využitím knihovny Expectations
    http://www.root.cz/clanky/programovaci-jazyk-clojure-testovani-s-vyuzitim-knihovny-expectations/

11. Odkazy na Internetu

  1. Expectations: příklady atd.
    http://jayfields.com/expectations/
  2. Expectations na GitHubu
    https://github.com/jaycfi­elds/expectations
  3. Lein-expectations na GitHubu
    https://github.com/gar3thjon3s/lein-expectations
  4. Testing Clojure With Expectations
    https://semaphoreci.com/blog/2014/09/23/tes­ting-clojure-with-expectations.html
  5. Clojure testing TDD/BDD libraries: clojure.test vs Midje vs Expectations vs Speclj
    https://www.reddit.com/r/Clo­jure/comments/1viilt/cloju­re_testing_tddbdd_librari­es_clojuretest_vs/
  6. Testing: One assertion per test
    http://blog.jayfields.com/2007/06/tes­ting-one-assertion-per-test.html
  7. Rewriting Your Test Suite in Clojure in 24 hours
    http://blog.circleci.com/rewriting-your-test-suite-in-clojure-in-24-hours/
  8. Clojure doc: zipper
    http://clojuredocs.org/clo­jure.zip/zipper
  9. Clojure doc: parse
    http://clojuredocs.org/clo­jure.xml/parse
  10. Clojure doc: xml-zip
    http://clojuredocs.org/clojure.zip/xml-zip
  11. Clojure doc: xml-seq
    http://clojuredocs.org/clo­jure.core/xml-seq
  12. Parsing XML in Clojure
    https://github.com/clojuredocs/guides
  13. Clojure Zipper Over Nested Vector
    https://vitalyper.wordpres­s.com/2010/11/23/clojure-zipper-over-nested-vector/
  14. Understanding Clojure's PersistentVector implementation
    http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation
  15. Understanding Clojure's PersistentHashMap (deftwice…)
    http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice.html
  16. Assoc and Clojure's PersistentHashMap: part ii
    http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html
  17. Ideal Hashtrees (paper)
    http://lampwww.epfl.ch/pa­pers/idealhashtrees.pdf
  18. Clojure home page
    http://clojure.org/
  19. Clojure (downloads)
    http://clojure.org/downloads
  20. Clojure Sequences
    http://clojure.org/sequences
  21. Clojure Data Structures
    http://clojure.org/data_structures
  22. 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
  23. 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
  24. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  25. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  26. 4Clojure
    http://www.4clojure.com/
  27. ClojureDoc (rozcestník s dokumentací jazyka Clojure)
    http://clojuredocs.org/
  28. Clojure (na Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  29. Clojure (na Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  30. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  31. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  32. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  33. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  34. 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
  35. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  36. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  37. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  38. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  39. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  40. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  41. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  42. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  43. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  44. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  45. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  46. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  47. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  48. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  49. Třída java.lang.String
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­g.html
  50. Třída java.lang.StringBuffer
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuffer.html
  51. Třída java.lang.StringBuilder
    http://docs.oracle.com/ja­vase/7/docs/api/java/lang/Strin­gBuilder.html
  52. StringBuffer versus String
    http://www.javaworld.com/ar­ticle/2076072/build-ci-sdlc/stringbuffer-versus-string.html
  53. Threading macro (dokumentace k jazyku Clojure)
    https://clojure.github.io/clo­jure/clojure.core-api.html#clojure.core/->
  54. Understanding the Clojure → macro
    http://blog.fogus.me/2009/09/04/un­derstanding-the-clojure-macro/
  55. clojure.inspector
    http://clojure.github.io/clo­jure/clojure.inspector-api.html
  56. The Clojure Toolbox
    http://www.clojure-toolbox.com/
  57. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  58. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  59. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  60. Leiningen: úvodní stránka
    http://leiningen.org/
  61. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  62. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  63. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  64. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  65. 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/
  66. 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/
  67. 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/
  68. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  69. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  70. 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/
  71. 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/
  72. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  73. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  74. 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/
  75. 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/
  76. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  77. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  78. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  79. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  80. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  81. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  82. 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/
  83. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
  84. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  85. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  86. Clojure.org: Atoms
    http://clojure.org/Atoms
  87. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  88. Transient Data Structureshttp://clojure.or­g/transients
Našli jste v článku chybu?
Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko

Podnikatel.cz: Jak otestovat e-shop. Víte?

Jak otestovat e-shop. Víte?

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

Vitalia.cz: Vím, co se učíš, ale netuším, co piješ

Vím, co se učíš, ale netuším, co piješ

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Podnikatel.cz: Tyto pojmy k #EET byste měli znát

Tyto pojmy k #EET byste měli znát

Lupa.cz: Blíží se konec Wi-Fi sítí bez hesla?

Blíží se konec Wi-Fi sítí bez hesla?

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

Podnikatel.cz: Takhle se prodávají mražené potraviny

Takhle se prodávají mražené potraviny

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

Vitalia.cz: Voda z Vltavy před a po úpravě na pitnou

Voda z Vltavy před a po úpravě na pitnou

Podnikatel.cz: „Lex Babiš“ Babišovi paradoxně pomůže

„Lex Babiš“ Babišovi paradoxně pomůže

Podnikatel.cz: Nemá dluhy? Zjistíte to na poště

Nemá dluhy? Zjistíte to na poště

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

Root.cz: Hořící telefon Samsung Note 7 zapálil auto

Hořící telefon Samsung Note 7 zapálil auto

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Lupa.cz: Adblock Plus začal prodávat reklamy

Adblock Plus začal prodávat reklamy

Podnikatel.cz: Udělali jsme velkou chybu, napsal Čupr

Udělali jsme velkou chybu, napsal Čupr