JReleaser Yksikkötestaus ja JUnit Mikko Mäkelä 6.11.2002
Sisältö Johdanto yksikkötestaukseen JUnit yleisesti JUnit Framework API (TestCase, TestSuite) Testien suorittaminen eri työkaluilla Teknisiä käytäntöjä testikeissien toteuttamiseen
Yksikkötestaus yleisesti Ideana suunnitella ja suorittaa testikeissejä ohjelmakoodin pienimmille yksiköille: yksittäiset luokat, metodit, proseduurit, funktiot ja moduulit Alunperin suunniteltu osaksi XP:n metodologiaa XPssä testikeissit koodataan ennen toiminnallisuutta ongelman ratkaisukeskeinen ajattelu testikeissien kirjoittaminen ei jää pakkopullaksi
Yksikkötestaus, jatkoa - Tavoitteena 100% testikattavuus - Jos komponentista puuttuu testikeissejä, se tulee olettaa ei-toimivaksi - Yhden testikeissin scope tulee olla riittävän pieni - jos et tiedä mitä testaisit, yrität testata kerralla liikaa - Riittävän pienillä testikeissellä ja sopivalla työkalulla (JUnit) päästään automatisoituun testaukseen
Yksikkötestaus käytännössä Alkuvaiheessa testien kirjoittaminen tuo merkittävää (häiritsevää) lisätyötä Yksikkötestauksen edut tulevat esiin, kun koodia joudutaan muokkamaan (mistä tahansa syystä)
JUnit yleisesti Ohjelmistokehikko Java-softan yksikkötestaukseen Open-source (IBM Common Public License 0.5) Coren muodostaa API ja valmiit työkalut Suuri käyttäjämäärä tuonut de-facto standardiksi Kevyt (binäärit 120kB) ja helppo käyttää Uusin Release on 3.8.1
JUnit, jatkoa JUnit ei poista testauksen suunnitteluun ja toteutukseen kuluvaa aikaa JUnit ei ymmärrä testattavan kohteen vaatimuksia eikä alkuperäistä ongelmaa Tarjoaa yhtenäisen APIn testitulosten välittämiseen ja hyvät työkalut testien ajamiseen/toistamiseen JUnit raportoi tulokset mustavalkoisesti (OK/FAIL)
Motivointi: JUnit vs. print-tulosteet JUnitilla testien toistaminen on merkittävästi tehokkampaa JUnitilla testien suorittajan ei tarvitse ymmärtää testauksen kohdetta ja sen toimintapaa vaatimukset on viety testikeissien sisälle alkuperäistä ohjelmoijaa ei tarvita
junit.framework.testcase Abstrakti kantaluokka, josta kaikki testikeissit periytetään Yksi testiluokka voi sisältää useita eri testejä, mutta niillä tulee olla yhteiset resurssit (instanssimuuttajat) Itse testit toteutetaan testxxx() -metodeissa Testien resussit initialisoidaan peittämällä setup() metodi ja vapautetaan peittämällä teardown()
TestCase, jatkoa Testauksen tulokset raportoidaan assertxxx() ja failxxx() metodeilla verrataan metodilta saatua paluuarvoa ja vaatimusten mukaista, oletettua paluuarvoa tarkastellaan poikkeusten esiintymistä siirretään päättely JUnitille tai tehdään se itse (fail)
TestCase, esimerkki (looginen XOR) public class MyTester extends TestCase { public void testbothtrue() { assertfalse(mylogic.xor(true, true); } public void testonetrue() { asserttrue(mylogic.xor(true, false); } public void testbothfalse() { assertfalse(mylogic.xor(false, false); } }
junit.framework.testsuite Abstrakti kantaluokka testikokoelmien toteuttamiseen Kukin kokoelma voi sisältää viittauksia 1. useisiin eri TestCase-luokkiin, jolloin kunkin TestCaseluokan kaikki testit (metodit) suoritetaan 2. vain valikoituihin testimetodeihin Testit lisätään kokoelmaan addxxx() -metodeilla
TestSuite, jatkoa Testien organisointi kokoelmiin voidaan tehdä useilla eri kriteereillä Moduuli-pohjainen organisointi (CoreSuite, UtilSuite, ViewSuite, DatabaseSuite) Vaatimusten prioriteetteihin perustuva organisointi (MinimalSuite, OptionalSuite, AllFeaturesSuite) Käyttötapauksiin perustuva organisointi
Poikkeukset ja niiden testaus 1/2 Jos poikkeuksia on määritelty, ne tulee myös testata osana implementaatiota Jos metodi ilmoittaa heittävänsä poikkeuksen tietyillä kriteereillä, testien tulee vaatia tätä Jos metodi ei APIn mukaan heitä poikkeuksia, niin sellaisia myöskään ei saa ilmetä
Poikkeukset ja niiden testaus 2/2 public void testindexoutofboundsexception() { Vector v= new Vector(10); try { Object o= v.elementat(v.size()); fail("should raise an } ArrayIndexOutOfBoundsException"); } catch (ArrayIndexOutOfBoundsException e) { }
Mitä testataan ja mitä ei? Periaatteessa kaikkeen tulisi kirjoittaa testikeissit Käytännön miniminä tulisi kattaa ainakin ulkopuolisille tarjottavien rajapintojen toteuttavat luokat ulkopuolinen käyttää rajapintaasi kuitenkin väärin core-luokat, kontrollerit, logiikkakomponentit laajassa käytössä olevat työkaluluokat util-luokassa oleva bugi voi näkyä useassa eri järjestelmässä
JUnit, tyypillisiä virheitä Perustapausta testaavien testikeissien kirjoittaminen on triviaalia, mutta tärkeää! Älä unohda tarkastella tapauksia, joissa argumentti on nolla tai null taulukko tai vektori on liian pieni/suuri kokoelma-luokka (java.util.iterator) sisältää väärän tyyppisiä objekteja (ClassCastException) Nimetty tiedosto tai resurssi puuttuu, ei toimi yms.
Testien suorittaminen, yleistä Testikeissejä ja niiden kokoelmia voidaan helposti ajaa eri työkaluilla junit.textui.testrunner (komentoriviltä) junit.awtui.testrunner (graafinen, AWT-pohjainen) junit.swingui.testrunner (graafinen, Swing-pohjainen) Ant:in kautta (käyttäen Taskia <junit>) IDE-kehittimen tarjoaman integraation kautta
Testien suorittaminen, Swing-GUI GUI käynnistetään komennolla ant test.gui Työkalu etsii automaattisesti kaikki luokkapolussa olevat testikeissit ja listaa ne Reload classes optiolla vältetään työkalun uudelleenkäynnistämisen tarve koodin muokkaamisen jälkeen
Testien suorittaminen Antin kautta Testit ajetaan komennolla ant test Ant paikallistaa kaikki luokkapolussa olevat testikeissit
Testiluokkien organisointi Testiluokat organisoinnissa huomioitavaa: testiluokkien selvä erottelu varsinaiseen koodiin nähden siten että nämä voidaan tarvittaessa erottaa yksinkertaisella regexp:llä testiluokan nimestä ja paketista tulisi ilmetä se luokka(t), johon testaaminen kohdistuu Esim. com.company.product.beans.mybean com.company.product.test.beans.mybeantester
Yksikkötestauksen nyrkkisääntö Jos edes harkitset lisääväsi koodiin print/debug tulostuksia määritteleväsi debugger-lausekkeita kirjoittavasi testiluokkia tai skriptejä KIRJOITA SEN SIJAAN TESTICASE!