Hlavní navigace

Programovací jazyk Clojure – testování s využitím knihovny Expectations

Pavel Tišnovský

V dnešní části seriálu o jazyce Clojure i o knihovnách, které jsou pro tento jazyk dostupné, se budeme zabývat dalším nástrojem určeným pro psaní jednotkových testů. Alternativou k již popsané knihovně clojure.test je nástroj nazvaný Expectations, který dokáže zpřehlednit jak psaní testů, tak i prezentaci výsledků.

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

8. Kolekce 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

12. Odkazy na Internetu

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:

  1. 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).
  2. 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).
  3. Test exception-test by měl být spuštěn bez chyby (všechny výjimky jsou skutečně vyhozeny).
  4. 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:

11. 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/

12. 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?

2. 9. 2015 21:33

Aha to je pravda, omlouvám se (vzniklo to tak, že jsem si až později uvědomil, že v repu chybí starý projekt factorial2, takže jsem provedl přejmenování, ale evidentně ne všude). V každém případě projekty v GIT repu jsou v pořádku.

Díky za upozornění!

2. 9. 2015 19:04

pěkné... jen tam u té první ukázky to nejde bezmyšlenkovitě copy-past, protože se tam v hlavičkách plete chybný název projektu "factorial2" ... je potřeba upravit to do tvaru:

(ns factorial3.core-test
  (:require [expectations :refer :all]
            [factorial3.core :refer :all]))
Měšec.cz: Exekuční poradna: ptejte se online

Exekuční poradna: ptejte se online

Root.cz: Nová třída SD karet A1 s vysokým výkonem

Nová třída SD karet A1 s vysokým výkonem

DigiZone.cz: R2B2 a Hybrid uzavřely partnerství

R2B2 a Hybrid uzavřely partnerství

DigiZone.cz: SES zajistí HD pro M7 Group

SES zajistí HD pro M7 Group

Lupa.cz: Levný tarif pro Brno nebude, je to kartel

Levný tarif pro Brno nebude, je to kartel

120na80.cz: Pánové, pečujte o svoje přirození a prostatu

Pánové, pečujte o svoje přirození a prostatu

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

120na80.cz: 5 nejčastějších mýtů o kondomech

5 nejčastějších mýtů o kondomech

Vitalia.cz: Nejlepší obranou při nachlazení je útok

Nejlepší obranou při nachlazení je útok

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Lupa.cz: Insolvenční řízení kvůli cookies? Vítejte v ČR

Insolvenční řízení kvůli cookies? Vítejte v ČR

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

DigiZone.cz: Česká televize mění schéma ČT :D

Česká televize mění schéma ČT :D

Vitalia.cz: Jak vybrat ořechy do cukroví a kde mají levné

Jak vybrat ořechy do cukroví a kde mají levné

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Vitalia.cz: Test na HIV je zdarma i za pět set

Test na HIV je zdarma i za pět set