15. joulukuu 2010, 11:28

Clojuren are-makro on fantastinen

Toivottavasti suurin osa teistä lukijoista tietää, kuinka kielellänne yksikkötestataan moduuleita. Vanhemmat kielet yleensä kaipaavat erillisiä kirjastoja, ja modernimmat kielet tukevat itsessään yksikkötestailuja. Ainakin Pythonissa on nättejä ratkaisuja sekä kielen että erilliskirjastojen puolesta. Olen kuullut myös Rubyn erinomaisesta, ellei parhaasta testauskirjastosta. Mutta Clojuren testauspuoli on aivan pahuksen erinomainen mielestäni. Se osaa todellakin hämärtää turhaa boilerplatea pois näkyvistä, ja testauskoodi on todellakin testauskoodia!

Nopeasti mietittynä syitä, miksi Clojuressa on miellyttävämpi kirjoittaa testejä kuin Javassa (verrokkini tässä artikkelissa):

  • Dynaaminen tyypitys. Monessa tapauksessa toki testien määrä vähenisi, jos funktioiden palautukset voisi määrätä staattisesti, mutta testailun suhteen on nopeata ainakin kirjoittaa nekin lisätestit
  • Dynaaminen kieli. Clojure on Lisp-sukulainen, joten ohjelman lisäksi itse kieltä voi ohjelmoida eteenpäin makroilla. Lisäksi testausympäristön voi toteuttaa DSL-kielenä, mikä typistää työtä entisestään

Clojure tarjoaa siis oman testausympäristön nimiavaruudessa clojure.test. Lyhyt johdatus tavanomaisiin testausmenetelmiin löytyy tästä artikkelista. Lyhyesti, kun staattisissa kielissä yleensä kirjoitellaan assertFoo-litanioita, clojuressa on simppeli ja näpäkkä is-makro. is ottaa vastaan vain totuusarvon, ja failaa testit, jos totuus on falskia. Tyypillinen esimerkki:

(is (= 2 3)) ;fail

Javassa (JUnit) vastaava voisi olla vaikkapa

assertTrue(2 == 3); //fail

Jos otamme nyt vähän realistisemman esimerkin, millä tavoin are helpottaa, niin oletetaan, että haluamme testailla kellonaikoja parsivaa funktiota, olkoon sen nimi parse-time. Tehdäksemme tästä vähän vaativampaa, parse-time palauttaa listan sijasta kuvauksen (vai mikähän olisi sopiva suomennos map-tietorakenteille). Nyt toistuva testailu muuttuukin yllättävän työlääksi!

(is (= (parse-time "10:43") {:hh 10, :mm 43}))

Tokihan tuosta on helppo lukea sinällään, mitä tapahtuu, mutta meidän ei todellakaan tarvitse toistaa yhtään ylimääräistä koodia Clojuressa, jos niin haluamme. Makroilla asia sujuu. Are-makrolle on jo varsin selväsanainen dokumentaatio olemassa. Haluan tässä nyt selventää, miten sitä kannattaa pikemminkin käyttää, jos vain testauksen kannalta mahdollista. Testataksemme funktiota parse-time vähän kattavammin, is-floodauksen sijaan muodostamme are-kuvauksen, jonka ytimenä toimii lispitön testikoodin sisältö. Vain ulkorakenteissa näemme Clojurea:

(are [hh mm teststr] (= {:hh hh :mm mm} (parse-time teststr))
   ;hh mm string
    12 00 "12"
    12 05 "12:05"
    00 00 ""
    00 00 ":"
    00 15 ":15"
    01 00 "1:"
)

Ja mitä näemme? Meidän tarvitsee kirjoittaa yksi ehtolause, jota sovelletaan argumenteille. Ja ainakin minusta nyt sopivan syntaksivärjäyksen kanssa koodi on superkaunista.

Oma mielipide? Clojurella päästään vielä pitkälle. Se on nykykielistä ehdottomasti fiksuin valinta, jos kestää funktionaalisen paradigman.

Tageja:

---
---

---

Aiheen vierestä