26 października 2010

Krótko o clojure.test

Logo ClojureZaczęło się od lektury Testing Clojure Code – Awesome “are”. Przypomniałem sobie, że jednym z pytań w trakcie mojego wykładu o Clojure podczas 70. spotkaniu Warszawa JUG było "Jak testować kod napisany w Clojure?" Wiemy, że Clojure kompilowany jest do bajtkodu, więc to, testujemy w Javie jest do wykorzystania z Clojure.

Jednak dla mnie pytanie wskazywało raczej potrzebę zrozumienia, jak to jest testować aplikacje Clojure w Clojure. Trafiłem na clojure.contrib.test-is, gdzie znalazłem informację, że teraz Clojure oferuje clojure.test.

Sprawdźmy, jak to działa. Krótko.
devmac:~ jacek$ clj
Clojure 1.2.0
user=> (use 'clojure.test)
nil
user=> ; Czy 5 == 4?
user=> (is (= 5 (+ 2 2)))

FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:5)
expected: (= 5 (+ 2 2))
  actual: (not (= 5 4))
false
user=> ; Test mianowany "Crazy arithmetic"
user=> (is (= 5 (+ 2 2)) "Crazy arithmetic")

FAIL in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:6)
Crazy arithmetic
expected: (= 5 (+ 2 2))
  actual: (not (= 5 4))
false
user=> ; Można również definiować zestawy testowe makrem deftest
user=> (deftest addition
  (is (= 4 (+ 2 2)))
  (is (= 7 (+ 3 4))))
#'user/addition
user=> ; ...i je uruchamiać funkcją run-tests
user=> (run-tests)

Testing user

Ran 1 tests containing 2 assertions.
0 failures, 0 errors.
{:type :summary, :test 1, :pass 2, :fail 0, :error 0}
Można też uruchomić testy z innej przestrzeni nazewniczej (aka pakietach w Clojure). W ten sposób oddzielamy sam test od testowanego kodu.
user=> ; Definiujemy nową przestrzeń testy
user=> (ns testy)
nil
testy=> (use 'clojure.test)
nil
testy=> ; W niej definiujemy zestaw testowy makrem deftest
testy=> (deftest addition
  (is (= 7 (+ 3 4))))
#'testy/addition
testy=> ; Zmieniamy aktualną przestrzeń
testy=> (ns user)
nil
user=> ; Wykonujemy testy z innej przestrzeni
user=> (run-tests 'testy)

Testing testy

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
{:type :summary, :test 1, :pass 1, :fail 0, :error 0}
p.s. Wczoraj wyszła wersja Clojure 1.3 Alpha 2, ale poza tym, że jest i podobno jest ciekawa, nic więcej :)

3 komentarze:

  1. Kod funkcyjny testuje sie bardzo fajnie za pomoca prostych algebraicznych wlasciwosci, ktorych spelnienie sie sprawdza (w przyblizeniu oczywiscie) za pomoca testow probabilistycznych.

    Przykladem takiej biblioteki jest ScalaCheck:
    http://code.google.com/p/scalacheck/

    Uzywam jej z powodzeniem w swoich projektach i uwazam, ze istnieje bardzo duza klasa problemow gdzie sprawdza sie znacznie lepiej niz jakikolwiek framework do testowania znany z Javy.

    OdpowiedzUsuń
  2. Początkowo przejrzałem stronę domową SC z pewnym powątpiewaniem i nie bardzo mogłem zrozumieć, co w tym takiego odkrywczego. Dopiero przeczytanie artykułów (patrz ScalaCheck Articles, a dokładnie tego pierwszego Tony Morris - Automated Unit Testing your Java using ScalaCheck) dało mi pogląd na ten temat.

    To jest obowiązkowe narzędzie, które od dzisiaj znajduje swoje miejsce w moim warsztacie programistycznym.

    Jak to ujął autor wspomnianego artykułu - w JUnit i temu podobnych testujemy prawdziwość naszych warunków, których liczba i dane zależą od chęci i pomysłowości autora. W ScalaCheck dane są generowane automatycznie, a my jedynie określamy warunki jakie musi spełniać nasz typ. Wspaniałe!

    Już tym ujął mnie za serce, ale kiedy padło magiczne słowo monada (w Luckily, generators are instances of what is known as a functor (also, a monad), so this task will be quite trivial.) przypomniałem sobie, że u tego Pana już kiedyś byłem - poznałem te lambdy w tytule jego bloga.

    Jest to kolejny przykład, że zabranie się za języki funkcyjne, pozwoliło mi na poszerzenie zestawu narzędzi, którymi operuję, a monady mają coś, czego bez Clojure na pewno bym nie dotknął.

    Dzięki Grześ!

    OdpowiedzUsuń
  3. Zatem ClojureCheck [http://kotka.de/blog/2010/06/ClojureCheck_is_back.html] może też Ci się spodobać :)

    OdpowiedzUsuń