Leiningen: nástroj pro správu projektů napsaných v Clojure (pluginy pro Leiningen)

Pavel Tišnovský 2. 4. 2015

V dnešní části článku o nástroji Leiningen určeného pro správu projektů vytvořených v programovacím jazyku Clojure si popíšeme některé užitečné pluginy Leiningenu. Připomeneme si existenci pluginu test2junit a dále se zaměříme na pluginy lein-ring, codox (generování dokumentace) a cloverage (zjištění pokrytí kódu testy).

Obsah

1. Leiningen: nástroj pro správu projektů napsaných v Clojure (pluginy pro Leiningen)

2. Plugin test2junit – jednotkové testy s výstupem kompatibilním s JUnit

3. Plugin lein-ring – automatizace úloh souvisejících s webovými aplikacemi

4. Automatické znovunačtení modulů při změně jejich zdrojového kódu

5. Vytvoření WAR souboru s webovou aplikací

6. Plugin codox – tvorba dokumentace v HTML formátu

7. Plugin cloverage – zjištění pokrytí kódu testy

8. Repositář s demonstračními příklady

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

10. Odkazy na Internetu

1. Leiningen: nástroj pro správu projektů napsaných v Clojure (pluginy pro Leiningen)

V dnešní části článku o programovacím jazyku Clojure i o nástroji Leiningen se vrátíme k problematice pluginů vytvořených pro Leiningen. Tento nástroj je (minimálně ve své druhé verzi) řešen modulárním způsobem, což mj. znamená, že do Leiningenu je možné „zaregistrovat“ prakticky libovolnou aplikaci sloužící například pro správu projektů, správu či monitorování běžících aplikací, generování dokumentace, zjištění pokrytí aplikace testy apod. Po instalaci pluginu je Leiningen rozšířen o další příkazy a parametry – ty lze považovat za nové úlohy, které lze přes příkaz lein spustit. Naprostá většina programátorů využívajících programovací jazyk Clojure a nástroj Leiningen sice vlastní pluginy nevytváří, na druhou stranu použití již naprogramovaných pluginů je velmi jednoduché a co je možná ještě podstatnější – některé pluginy dokážou urychlit a zpříjemnit vývoj. Seznam pluginů lze nalézt na adrese https://github.com/techno­mancy/leiningen/wiki/Plugin­s.

2. Plugin test2junit – jednotkové testy s výstupem kompatibilním s JUnit

O pluginu nazvaném test2junit jsme se již zmiňovali v předchozích částech tohoto článku, takže si dnes jen stručně řekněme, v čem spočívá význam tohoto modulu. Leiningen již ve své původní variantě podporuje spouštění testů, ovšem formát výsledků testů není kompatibilní s dnes de facto standardem představovaným nástrojem JUnit. Právě z tohoto důvodu je plugin test2junit velmi užitečný, protože taktéž dokáže spustit testy a následně vygenerovat XML soubor s výsledky, přičemž formát tohoto souboru je kompatibilní s JUnit. Výsledný soubor je možné dále zpracovat dalšími nástroji, například lze velmi snadno přidat nový job s testy aplikace vytvořené v programovacím jazyku Clojure do Jenkinsu atd.

Pro úplnost se podívejme na jednoduchý demonstrační příklad obsahující jednu funkci, která je následně otestována dvojicí testů, u nichž se předpokládá pozitivní výsledek a dvojicí testů, u nichž se naopak očekává výsledek negativní.

Soubor project.clj. Zde si povšimněte především deklarace používaného pluginu – jedná se o zvýrazněný řádek (všechny pluginy Leiningenu se zapisují pod tento klíč):

(defproject factorial "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"]]
  :main ^:skip-aot factorial.core
  :target-path "target/%s"
  :plugins [[test2junit "1.1.0"]]
  :profiles {:uberjar {:aot :all}})

Zdrojový kód demonstračního příkladu (ten již známe z předchozích částí článku):

(ns factorial.core
  (:gen-class))
 
(defn factorial
    [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))))

Testy uložené v adresáři test/factorial; povšimněte si, že jsou použity jak pozitivní, tak i negativní testy (u nichž je zaručeno, že neprojdou):

(ns factorial.core-test
  (:require [clojure.test :refer :all]
            [factorial.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)))))

Spuštění testů běžným způsobem:

lein test
 
lein test factorial.core-test
 
lein test :only factorial.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 factorial.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 factorial.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 factorial.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 factorial.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 factorial.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.
 
Tests failed.

Spuštění testů s vygenerováním výsledku kompatibilního s JUnit:

lein test2junit

Vygenerovaný soubor s výsledky testů:

<?xml version="1.0" encoding="UTF-8"?>
<testsuite name="factorial.core-test" errors="0" failures="6" tests="4" time="0.0669" timestamp="2015-04-01_19:29:27+0200">
    <testcase name="negative-exception-test" classname="factorial.core-test" time="0.0106">
    <failure>expected: (thrown? IllegalArgumentException (factorial 1))
  actual: nil
      at: AFn.java:18</failure>
    <failure>expected: (thrown? IllegalArgumentException (factorial 2))
  actual: nil
      at: AFn.java:18</failure>
    <failure>expected: (thrown? IllegalArgumentException (factorial 3))
  actual: nil
      at: AFn.java:18</failure>
    </testcase>
    <testcase name="negative-factorial-test" classname="factorial.core-test" time="0.0064">
    <failure message="negative test case #1">negative test case #1
expected: (= (factorial 0) 0)
  actual: (not (= 1 0))
      at: AFn.java:18</failure>
    <failure message="negative test case #2">negative test case #2
expected: (= (factorial 1) 0)
  actual: (not (= 1 0))
      at: AFn.java:18</failure>
    <failure message="negative test case #3">negative test case #3
expected: (= (factorial 2) 0)
  actual: (not (= 2 0))
      at: AFn.java:18</failure>
    </testcase>
    <testcase name="factorial-test" classname="factorial.core-test" time="0.0025">
    </testcase>
    <testcase name="exception-test" classname="factorial.core-test" time="0.0013">
    </testcase>
</testsuite>

3. Plugin lein-ring – automatizace úloh souvisejících s webovými aplikacemi

Dalším zajímavým pluginem je modul nazvaný lein-ring. Název tohoto přídavného modulu již alespoň částečně napovídá, že se jedná o plugin určený pro správu aplikací využívajících Clojure Ring, což je knihovna používaná především pro tvorbu webových aplikací (možnosti této knihovny jsme si již poměrně dopodrobna popsali v předchozích dílech tohoto článku). Vraťme se však k modulu lein-ring. Po jeho instalaci je nástroj Leiningen rozšířen o další příkazy i o další funkcionalitu. Mezi nově nabízené možnosti patří zejména:

  1. spuštění webového serveru, nasazení aplikace a spuštění prohlížeče s otevřenou aplikací
  2. dtto, ovšem bez spuštění prohlížeče (takzvaný režim headless)
  3. detekce změn ve zdrojovém kódu a automatický update aplikace nasazené na serveru
  4. vytvoření souboru .war (web archive) určeného pro nasazení do servlet kontejneru
  5. vytvoření souboru „uberwar“, což je obdoba předchozího archivu, ovšem obsahujícího i všechny potřebné knihovny

Dvě z těchto vlastností si popíšeme v navazujících dvou kapitolách.

4. Automatické znovunačtení modulů při změně jejich zdrojového kódu

Nejprve si odzkoušíme první tři funkce nabízené výše zmíněným pluginem lein-ring. S pomocí příkazu lein new app ringapp1 vytvoříme nový projekt a upravíme jeho projektový soubor takovým způsobem, aby se používal plugin lein-ring. Navíc je zde uveden řádek začínající klíčem :ring, kterým je naznačeno, kde má plugin hledat definici webové aplikace. Tento řádek je využit při automatických změnách aplikace nasazené na webovém serveru ve chvíli, kdy se detekuje změna ve zdrojových kódech:

(defproject ringapp1 "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"]
                 [ring/ring-core "1.3.2"]
                 [ring/ring-jetty-adapter "1.3.2"]
                 [hiccup "1.0.4"]
                 [org.clojure/tools.cli "0.3.1"]]
  :dev-dependencies [[lein-ring "0.8.10"]]
  :plugins [[lein-ring "0.8.10"]]
  :main ^:skip-aot ringapp1.core
  :target-path "target/%s"
  :ring     {:handler ringapp1.core/app}
  :profiles {:uberjar {:aot :all}})

Kód webové aplikace je poměrně jednoduchý a navíc jsme se s ním již setkali při popisu knihoven Clojure RingHiccup. Jedná se o aplikaci zobrazující tabulku faktoriálů, přičemž uživatel může ve webovém formuláři specifikovat rozsah vstupních hodnot:

(ns ringapp1.core
  (:gen-class))
 
(require '[ring.adapter.jetty     :as jetty])
(require '[ring.middleware.params :as http-params])
(require '[ring.util.response     :as response])
 
(require '[hiccup.page :as page])
(require '[hiccup.form :as form])
 
(defn fact
    [n]
    (apply * (range 1M (inc n))))
 
(defn html-page
    [max-n]
    (page/xhtml
        [:head
            [:title "Ring app #1"]
            [:meta {:name "Generator" :content "Clojure"}]
            [:meta {:http-equiv "Content-type" :content "text/html; charset=utf-8"}]
        ]
        [:body
            [:h1 "Ring app #1"]
            (form/form-to [:get "/"]
                (form/text-field {:size "20"} "max-n" max-n)
                [:br]
                (form/submit-button "Recalculate"))
            [:br]
            [:table {:style "border:2px solid brown;background-color:#ace"}
                [:tr [:th "n"] [:th "n!"]]
                (for [n (range 0M (inc max-n))]
                    [:tr [:td n] [:td {:style "text-align:right"} (fact n)]])
            ]
        ]))
 
(defn param->number
    "Prevod parametru specifikovaneho v param-name na cislo typu BigDecimal."
    [params param-name]
    (let [param (get params param-name)]
        (try
            (bigdec param)             ; pokus o prevod na BigDecimal
            (catch Exception e nil)))) ; pokud se prevod nepovede, vraci se nil
 
(defn handler
    "Zpracovani pozadavku."
    [request]
    (let [params (:params request)
          max-n  (param->number params "max-n")]
        (println "Params: " params)
        (println "max-n:  " max-n)
        (-> (response/response (html-page (if max-n max-n 10M)))
            (response/content-type "text/html; charset=utf-8"))))
 
(def app
    "Datova struktura predstavujici kostru webove aplikace."
    (-> handler
        http-params/wrap-params))
 
(defn -main
    "Spusteni webove aplikace na portu 8080."
    [& args]
    (jetty/run-jetty app {:port 8080}))

Tuto aplikaci je možné spustit různými způsoby. Základní způsob již známe:

lein run

Na standardním výstupu by se měly objevit následující zprávy:

2015-04-01 21:25:36.267:INFO:oejs.Server:jetty-7.6.13.v20130916
2015-04-01 21:25:36.345:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:8080

Vidíme, že aplikace je otevřena na portu 8080.

Alternativně lze aplikaci spustit příkazem zajišťujícím provedení spuštění přes plugin lein-ring:

lein ring server

Po provedení tohoto příkazu by se měly stát dvě věci – měl by se otevřít webový prohlížeč s aplikací a paralelně s tím by se měly na standardním výstupu objevit informace o spuštění na portu 3000. Poslední dva řádky vypisuje samotná aplikace, protože byla otevřena v prohlížeči a tudíž klient poslal dotaz(y) serveru:

2015-04-01 21:26:11.472:INFO:oejs.Server:jetty-7.6.13.v20130916
2015-04-01 21:26:11.494:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000
Started server on port 3000
Params:  {}
max-n:   nil
Params:  {}
max-n:   nil

Alternativně je možné následujícím příkazem provést prakticky stejnou operaci, akorát bez spuštění prohlížeče:

lein ring server-headless
2015-04-01 21:26:42.900:INFO:oejs.Server:jetty-7.6.13.v20130916
2015-04-01 21:26:42.923:INFO:oejs.AbstractConnector:Started SelectChannelConnector@0.0.0.0:3000
Started server on port 3000

Proč je však vhodné aplikaci spouštět přes lein ring server a nikoli pomocí pouhého lein run? Rozdíl spočívá v tom, že plugin lein-ring detekuje změny provedené ve zdrojových kódech a ihned tyto změny aplikuje na běžící aplikaci. To je velmi užitečné, ať již při ladění, tak i při interaktivním (či polointeraktivním) způsobu vývoje. Teoreticky je možné takto spuštěnou aplikaci nasadit na server a případné požadavky uživatelů řešit na „živém kódu“ :-) (kupodivu se to pro některé aplikace, zejména intranetové, osvědčilo).

5. Vytvoření WAR souboru s webovou aplikací

Další zajímavou funkcí modulu lein-ring je možnost vytvoření webových archivů (WAR) obsahujících aplikaci přeloženou do bajtkódu Javy. Takto zabalenou aplikaci lze nasadit na prakticky jakýkoli servlet kontejner, například na Tomcat. Vytvoření archivu je velmi snadné:

lein clean
lein ring war
 
Created /home/ptisnovsk/repos/clojure-examples/leinring1/target/ringapp1-0.1.0-SNAPSHOT.war

Vytvořený soubor má velikost 368kB a po jeho prohlédnutí je patrné, že skutečně obsahuje soubory .class a několik nezbytných konfiguračních souborů:

ls -l target/*.war
-rw-rw-r--. 1 ptisnovs ptisnovs 376389 Apr  1 21:30 target/ringapp1-0.1.0-SNAPSHOT.war

Ve chvíli, kdy je nutné nasadit aplikaci na servlet kontejner neobsahující všechny potřebné knihovny, může být důležitější použití dalšího příkazu:

lein clean
lein ring uberwar
 
Created /home/ptisnovsk/repos/clojure-examples/leinring1/target/ringapp1-0.1.0-SNAPSHOT-standalone.war

Tento příkaz vytvoří mnohem větší soubor (5,3MB) obsahující kromě přeložené aplikace i všechny potřebné knihovny:

ls -l target/*.war
-rw-rw-r--. 1 ptisnovs ptisnovs 5472405 Apr  1 21:31 target/ringapp1-0.1.0-SNAPSHOT-standalone.war
-rw-rw-r--. 1 ptisnovs ptisnovs  376389 Apr  1 21:30 target/ringapp1-0.1.0-SNAPSHOT.war

6. Plugin codox – tvorba dokumentace v HTML formátu

Třetí přídavný modul určený pro nástroj Leiningen, s nímž se v dnešním článku seznámíme, se jmenuje codox. Tento plugin je v současnosti velmi populární, protože slouží pro tvorbu programové dokumentace pro mnoho knihoven naprogramovaných v Clojure. Dokumentace je vygenerována do souborů HTML, jedná se tedy o obdobu JavaDocu (tvorba programové dokumentace pro javovské aplikace) popř. Doxygenu (podobný nástroj určený pro programovací jazyky C, C++, Python, PHP atd.), samozřejmě s tím rozdílem, že codox pracuje se jmennými prostory, funkcemi, globálními deklaracemi i – alespoň do určité míry – s makry.

Podívejme se nyní na to, jak může vygenerovaná dokumentace vypadat. Vytvoříme novou aplikaci:

lein new app codoxtest

V aplikaci vytvoříme tři moduly: src/codoxtest/core.clj, src/codoxtest/module_a.cljsrc/codoxtest/module_b.clj s prakticky stejnými proměnnými, makrem a funkcemi:

(ns codoxtest.core
    "Popis modulu 'core'")
 
(defn -main
    "I don't do a whole lot ... yet."
    [& args]
    (println "Hello, World!"))
 
(defn f1
    "Popis funkce f1."
    [])
 
(defn f2
    "Popis funkce f2."
    [x]
    (inc x))
 
(defn f3
    "Popis funkce f2."
    [x y]
    (+ x y))
 
(def answer
    "The Answer to the Ultimate Question of Life, the Universe, and Everything"
    42)
 
(defmacro unless
    "Makro prevzate z dokumentace Clojure."
    [pred a b]
    `(if (not ~pred) ~a ~b))
(ns codoxtest.module-a
    "Popis modulu se jmenem 'module-a'")
 
(defn f1
    "Popis funkce f1."
    [])
 
(defn f2
    "Popis funkce f2."
    [x]
    (inc x))
 
(defn f3
    "Popis funkce f2."
    [x y]
    (+ x y))
 
(def answer
    "The Answer to the Ultimate Question of Life, the Universe, and Everything"
    42)
 
(defmacro unless
    "Makro prevzate z dokumentace Clojure."
    [pred a b]
    `(if (not ~pred) ~a ~b))
(ns codoxtest.module-b
    "Popis modulu se jmenem 'module-b'")
 
(defn f1
    "Popis funkce f1."
    [])
 
(defn f2
    "Popis funkce f2."
    [x]
    (inc x))
 
(defn f3
    "Popis funkce f2."
    [x y]
    (+ x y))
 
(def answer
    "The Answer to the Ultimate Question of Life, the Universe, and Everything"
    42)
 
(defmacro unless
    "Makro prevzate z dokumentace Clojure."
    [pred a b]
    `(if (not ~pred) ~a ~b))

Projektový soubor project.clj musí vypadat následovně (povšimněte si nového pluginu):

(defproject codoxtest "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"]]
  :main ^:skip-aot codoxtest.core
  :target-path "target/%s"
  :plugins [[codox "0.8.11"]]
  :profiles {:uberjar {:aot :all}})

Vygenerování dokumentace se provede příkazem:

lein doc
Generated HTML docs in /home/ptisnovsk/repos/clojure-examples/codoxtest/doc

Vygenerovanou dokumentaci je možné nalézt na adrese https://github.com/tisnik/clojure-examples/tree/master/codoxtest/doc.

Obrázek 1: Vygenerovaná dokumentace – seznam všech modulů.

Obrázek 2: Vygenerovaná dokumentace – popis vybraného modulu.

7. Plugin cloverage – zjištění pokrytí kódu testy

Posledním pluginem určeným pro nástroj Leiningen, který si dnes popíšeme, je modul nazvaný cloverage. Úkolem tohoto modulu je zjištění, které části programového kódu jsou pokryté testy, tj. opět se jedná o analogii k podobným nástrojům existujícím i pro další programovací jazyky, ovšem s tím rozdílem, že kvůli použití maker je zjištění pokrytí testy v programovacím jazyku Clojure složitější.

Funkci tohoto pluginu otestujeme jednoduše – použijeme upravený projekt pro výpočet faktoriálu, do nějž jsou přidány další dvě totožné funkce, které se od sebe odlišují pouze jménem:

(ns cloverage.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))))

Projektový soubor project.clj musí vypadat následovně (opět si povšimněte nového pluginu):

(defproject cloverage "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"]]
  :main ^:skip-aot cloverage.core
  :target-path "target/%s"
  :plugins [[lein-cloverage "1.0.2"]]
  :profiles {:uberjar {:aot :all}})

Nejzajímavější jsou jednotkové testy. Povšimněte si, že funkce factorial1 je otestována celá, tj. včetně obou větví, funkce factorial2 je otestována jen částečně (pouze jedna větev) a nakonec funkce factorial3 není otestována vůbec. Tyto rozdíly by se měly projevit ve výsledcích:

(ns cloverage.core-test
  (:require [clojure.test :refer :all]
            [cloverage.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 factorial2-test
    (testing "Factorial"
        (is ( = (factorial2 0)   1) "beginning")
        (is ( = (factorial2 1)   1) "beginning")
        (is ( = (factorial2 2)   (* 1 2)) "still easy")
        (is ( = (factorial2 5)   (* 1 2 3 4 5)) "5!")
        (is ( = (factorial2 6)   720) "6!")))
 
(deftest exception-test
    (testing "If factorial throws exception"
        (is (thrown? IllegalArgumentException (factorial -1)))
        (is (thrown? IllegalArgumentException (factorial -2)))
        (is (thrown? IllegalArgumentException (factorial -100)))))

Nyní je nutné spustit novou úlohu:

lein cloverage
Loading namespaces:  (cloverage.core)
Test namespaces:  (cloverage.core-test)
Loaded  cloverage.core  .
Instrumented namespaces.
 
Testing cloverage.core-test
 
Ran 3 tests containing 13 assertions.
0 failures, 0 errors.
Ran tests.
Produced output in /home/ptisnovsk/repos/clojure-examples/cloverage/target/coverage .
HTML: file:///home/ptisnovsk/repos/clojure-examples/cloverage/target/coverage/index.html

Podívejme se nyní na výsledky – zdá se, že skutečně odpovídají testům:

Obrázek 3: Pokrytí zdrojového kódu testy – celková statistika.

widgety

Obrázek 4: Pokrytí zdrojového kódu testy – zelené řádky byly vyhodnoceny, červené nikoli (bílé řádky nepředstavují zdrojový kód).

8. Repositář s demonstračními příklady

Všechny dnes popsané demonstrační příklady byly, podobně jako v minulé i předminulé části tohoto článku, uloženy do GIT repositáře dostupného na adrese https://github.com/tisnik/clojure-examples (dříve popsané příklady budou přidány později). V tabulce zobrazené pod tímto odstavcem naleznete na jednotlivé příklady přímé odkazy:

9. Odkazy na předchozí části 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/

10. Odkazy na Internetu

  1. Plugin cloverage
    https://github.com/lshift/cloverage
  2. Plugin codox
    https://github.com/weavejester/codox
  3. Plugin lein-ring
    https://github.com/weavejester/lein-ring
  4. Plugin test2junit
    https://github.com/ruediger­gad/test2junit
  5. java.jdbc API Reference
    https://clojure.github.io/java.jdbc/
  6. Hiccup
    https://github.com/weavejester/hiccup
  7. Clojure Ring na GitHubu
    https://github.com/ring-clojure/ring
  8. A brief overview of the Clojure web stack
    https://brehaut.net/blog/2011/rin­g_introduction
  9. Getting Started with Ring
    http://www.learningclojure­.com/2013/01/getting-started-with-ring.html
  10. Getting Started with Ring and Compojure – Clojure Web Programming
    http://www.myclojureadven­ture.com/2011/03/getting-started-with-ring-and-compojure.html
  11. Unit Testing in Clojure
    http://nakkaya.com/2009/11/18/unit-testing-in-clojure/
  12. Testing in Clojure (Part-1: Unit testing)
    http://blog.knoldus.com/2014/03/22/tes­ting-in-clojure-part-1-unit-testing/
  13. API for clojure.test – Clojure v1.6 (stable)
    https://clojure.github.io/clo­jure/clojure.test-api.html
  14. Leiningen: úvodní stránka
    http://leiningen.org/
  15. Leiningen: Git repository
    https://github.com/techno­mancy/leiningen
  16. leiningen-win-installer
    http://leiningen-win-installer.djpowell.net/
  17. Clojure 1: Úvod
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm/
  18. Clojure 2: Symboly, kolekce atd.
    http://www.root.cz/clanky/clojure-aneb-jazyk-umoznujici-tvorbu-bezpecnych-vicevlaknovych-aplikaci-pro-jvm-2-cast/
  19. 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/
  20. 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/
  21. 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/
  22. Clojure 6: Podpora pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-6-futures-nejsou-jen-financni-derivaty/
  23. Clojure 7: Další funkce pro paralelní programování
    http://www.root.cz/clanky/programovaci-jazyk-clojure-7-dalsi-podpurne-prostredky-pro-paralelni-programovani/
  24. 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/
  25. 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/
  26. Clojure 10: Kooperace mezi Clojure a Javou
    http://www.root.cz/clanky/programovaci-jazyk-clojure-10-kooperace-mezi-clojure-a-javou-pokracovani/
  27. Clojure 11: Generátorová notace seznamu/list comprehension
    http://www.root.cz/clanky/programovaci-jazyk-clojure-11-generatorova-notace-seznamu-list-comprehension/
  28. 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/
  29. Clojure 13: Překlad programů z Clojure do bajtkódu JVM II
    2) http://www.root.cz/clanky/programovaci-jazyk-clojure-13-preklad-programu-z-clojure-do-bajtkodu-jvm-pokracovani/
  30. Clojure 14: Základy práce se systémem maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-14-zaklady-prace-se-systemem-maker/
  31. Clojure 15: Tvorba uživatelských maker
    http://www.root.cz/clanky/programovaci-jazyk-clojure-15-tvorba-uzivatelskych-maker/
  32. Clojure 16: Složitější uživatelská makra
    http://www.root.cz/clanky/programovaci-jazyk-clojure-16-slozitejsi-uzivatelska-makra/
  33. Clojure 17: Využití standardních maker v praxi
    http://www.root.cz/clanky/programovaci-jazyk-clojure-17-vyuziti-standardnich-maker-v-praxi/
  34. Clojure 18: Základní techniky optimalizace aplikací
    http://www.root.cz/clanky/programovaci-jazyk-clojure-18-zakladni-techniky-optimalizace-aplikaci/
  35. Clojure 19: Vývojová prostředí pro Clojure
    http://www.root.cz/clanky/programovaci-jazyk-clojure-19-vyvojova-prostredi-pro-clojure/
  36. 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/
  37. Clojure 21: ClojureScript aneb překlad Clojure do JS
    http://www.root.cz/clanky/programovaci-jazyk-clojure-21-clojurescript-aneb-preklad-clojure-do-javascriptu/
Našli jste v článku chybu?
Root.cz: Hořící telefon Samsung Note 7 zapálil auto

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

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

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

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

Vitalia.cz: Tesco nabízí desítky tun jídla zdarma

Tesco nabízí desítky tun jídla zdarma

Vitalia.cz: Tradiční čínská medicína a rakovina

Tradiční čínská medicína a rakovina

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

120na80.cz: Co je padesátkrát sladší než cukr?

Co je padesátkrát sladší než cukr?

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

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

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

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

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

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

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

Podnikatel.cz: Instalatér, malíř a elektrikář. "Vymřou"?

Instalatér, malíř a elektrikář. "Vymřou"?

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

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

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

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Vitalia.cz: Jsou vegani a vyrábějí nemléko

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