Olio-ohjelmien testaamisesta Mika Katara et. al. Ohjelmistotekniikan laitos Tampereen teknillinen yliopisto 13.8.2013 Ohjelmistojen testaus, 2013 1(32)
Sisällysluettelo 1/2 Olio-ohjelmien testaamisesta tarkemmin 4 Yleistä 5 Oliojärjestelmien hyviä puolia testaajan näkökulmasta 6 Oliolla on tila ja elinkaari 7 Tiedon kätkentä 8 Perintä 9 Polymorfismi ja dynaaminen sitominen 10 Abstraktit luokat 11 Poikkeukset 12 Rinnakkaisuus 13 Erityispiirteiden testauksen strategia 14 Yksikkötestaus 15 Luokan yksikkötestaus 16 Luokkien integrointitestaus 19
Sisällysluettelo 2/2 Oliotestauksen skaalaaminen riskien suhteen 22 Mock-oliot 25 Testauspatternit 32
Olio-ohjelmien testaamisesta tarkemmin Nykyään suurin osa ohjelmistoista suunnitellaan ja toteutetaan käyttäen oliotekniikoita. Miten oliojärjestelmien testaus eroaa perinteisistä proseduraalisista ohjelmista? Ohjelmistojen testaus, 2013 4(32)
Yleistä Pääosin mukailtu lähteestä M. Pezzè, M. Young: Software Testing and Analysis: Process, Principles, and Techniques Oliot vaikuttavat testaukseen V-mallin alimmilla tasoilla Lähinnä yksikkö- ja integrointitestauksessa Muissa vaiheissa perinteiset menetelmät purevat hyvin Pohjimmiltaan tekniikat proseduraalisten ohjelmien testaukseen toimivat myös olio-ohjelmien tapauksessa Olioiden erityispiirteistä johtuen joitain tärkeitä kohtia saattaa kuitenkin jäädä testaamatta Tästä syystä on kehitetty joukko oliospesifisiä tekniikoita, joilla nämä muuten katveeseen jäävät alueet saadaan testattua Vaikuttavat testaukseen lähinnä yksikkö- ja integrointitasoilla Ohjelmistojen testaus, 2013 5(32)
Oliojärjestelmien hyviä puolia testaajan näkökulmasta Olio-ohjelmoinnin myötä on alettu kiinnittää enemmän huomiota ohjelman suunnitteluun (suunnittelumallit yms.) Toisaalta, sama ongelma voidaan ratkaista hyvin eri tavoilla Metodit ovat yleensä lyhyempiä kuin vastaavat proseduurit Yleensä metodin lyhyys tarkoittaa helpompaa yksikkötestausta Toisaalta niitä voi olla enemmän Testitapaukset ovat potentiaalisesti uudelleenkäytettäviä siinä missä koodikin Ohjelmistojen testaus, 2013 6(32)
Oliolla on tila ja elinkaari Suurin olio-ohjelmien erityispiirre testauksen näkökulmasta on se, että oliolla on tila Järjestelmän käyttäytyminen tietyllä hetkellä riippuu siitä, mitkä ovat sen olioiden sisäiset tilat sillä hetkellä Tekniikat, jotka eivät ota huomioon olioiden tilaa, eivät yksinään välttämättä riitä oliojärjestelmien testaamiseen Olion elinkaari rakentamisesta purkamiseen Ohjelmistojen testaus, 2013 7(32)
Tiedon kätkentä Osa olion muuttujista, jotka määrittelevät sen tilan, on kätketty ulkopuolisilta Tiedon kätkentä voi parantaa koodin laatua ja sitä kautta vähentää testauksen tarvetta Täytyy kuitenkin huolehtia siitä, että myös kätkettyjen muuttujien arvot tulevat tarkastettua kun tutkitaan miten järjestelmä käyttäytyy Ohjelmistojen testaus, 2013 8(32)
Perintä Pitääkö testata kaikki perinnän tasot? Sovelluksilla lapsiluokissa on ohjelman toteutus. Kirjastoilla kantaluokat kaikkein kriittisimpiä. Testaajien täytyy päättää Tarvitseeko lapsiluokan metodeja varten kehittää uusia testitapauksia Tarvitseeko kantaluokan testitapauksia ajaa uudelleen lapsiluokalle Mitä lapsiluokan metodeja ei tarvitse testata Testaajan sääntö: testattavat asiat valitaan sen mukaan, mitä tietoa tarvitaan Ohjelmistojen testaus, 2013 9(32)
Polymorfismi ja dynaaminen sitominen Muuttujan edustaman olion luokka ei ole selvillä käännösaikana, joten siihen tehtävän metodikutsun todellinen kohde päätetään vasta ajoaikana järjestelmän tilan perusteella Tämä hankaloittaa suunnittelun ja koodin ymmärtämistä Perinteiset tekniikat, jotka olettavat sitomisen olevan staattista, eivät kata kaikkia mahdollisia sitomisia (käyttäytymisiä) Eri sidontamahdollisuudet tulee siis testata Voitaisiinko soveltaa päätöskattavuutta? Useamman kutsun tapauksessa myös niiden mahdolliset sitomiskombinaatiot tulisi testata Ohjelmistojen testaus, 2013 10(32)
Abstraktit luokat Abstraktit luokat voivat olla tärkeitä osasia kirjastojen tai komponenttien rajapinnoissa, mutta niitä ei voida sinänsä instantioida tai testata Joskus voi olla mahdotonta tietää kuinka ko. luokkia tullaan todellisuudessa käyttämään Testaajien (luokkien kehittäjien) täytyy yrittää tehdä valistuneita arvauksia tärkeistä käyttötavoista ja testata sen mukaisesti Ohjelmistojen testaus, 2013 11(32)
Poikkeukset Oliokielten poikkeusmekanismeja hyödynnetään yleisesti Poikkeus voidaan heittää aivan toisaalla lähdekoodissa kuin missä se käsitellään Dynaaminen sitominen tuo mukanaan oman monimutkaisuutensa Poikkeustapausten testaamiseen on syytä kiinnittää erityistä huomiota Ohjelmistojen testaus, 2013 12(32)
Rinnakkaisuus Oliokielissä säikeiden käyttöön kannustetaan tai sitä jopa vaaditaan Esim. Javan käyttöliittymäkirjastot Valitettavasti rinnakkaisuus on testauksen kannalta varsinainen viidakko Yleensä tapahtuma A tapahtuu ennen tapahtumaa B, mutta pitääkö tämä aina paikkansa vai riippuuko se vuorontajasta? Toimiiko keskinäinen poissulkeminen kaikissa tilanteessa? Ohjelmistojen testaus, 2013 13(32)
Erityispiirteiden testauksen strategia Nyrkkisääntönä voidaan sanoa, että kukin edellä mainittu erityispiirre pitäisi testata erikseen Poikkeuksen muodostavat piirteet, jotka interferoivat keskenään kuten perintä ja tilasta riippuva käyttäytyminen Ohjelmistojen testaus, 2013 14(32)
Yksikkötestaus Metodeita ei yleensä kannata ajatella testattavina yksikköinä, koska niiden käyttäytyminen riippuu usein ympäröivän olion tilasta Luokka tuntuu sopivammalta testattavalta yksiköltä Priorisointi kannattaa muistaa; kaikki luokat eivät ole yhtä tärkeitä Luokkaa ei voi testata testaamatta sen metodeita Ohjelmistojen testaus, 2013 15(32)
Luokan yksikkötestaus 1/3 Jos luokka on abstrakti, instantioidaan sen lapsiluokista olioita joiden avulla luokka testataan Mikäli lapsiluokkia ei ole, niitä pitää määritellä pelkästään testausta varten Suunnittele testitapaukset, jotka testaavat perittyjen ja ylimääriteltyjen metodien kutsumista Arvioi, mitkä perityistä metodeista pitää testata uudelleen Arvioi, voidaanko kantaluokan testitapauksia uudelleenkäyttää Älä unohda rakentajia ja purkajia (resurssien vapauttaminen) Ohjelmistojen testaus, 2013 16(32)
Luokan yksikkötestaus 2/3 Harmaalaatikkotestaus: mikäli luokan käyttäytyminen on spesifioitu tilakoneen avulla, suunnittele joukko testitapauksia ko. spesifikaation avulla Mikäli tilakonespesifikaatiota ei ole, sellainen ehkä kannattaa tehdä testauksen helpottamiseksi Lasilaatikkotestaus: täydennä testitapausten joukkoa käyttäen apuna koodia ja sen rakennetta Suunnittele testitapauksia, jotka testaavat poikkeustenkäsittelyä Poikkeukset, joita tämän luokan metodit voivat heittää, ottaa kiinni ja käsitellä Ohjelmistojen testaus, 2013 17(32)
Luokan yksikkötestaus 3/3 Täydennä testitapausten joukkoa testeillä, jotka testaavat tämän luokan metodeihin kohdistuvia polymorfia metodikutsuja Kannattaa kiinnittää huomiota myös luokan sisällä tapahtuvaan vuorovaikutukseen Metodit voivat käyttää samoja muuttujia Metodit voivat kutsua toisia, saman luokan metodeita Ohjelmistojen testaus, 2013 18(32)
Luokkien integrointitestaus 1/3 Vrt. kokoava inkrementaalinen integrointistrategia Aloitetaan pienistä luokkien muodostamista klustereista, joiden kokoa kasvatetaan vähitellen Ensin pitää kuitenkin selvittää luokkien väliset riippuvuudet Luokka A riippuu B:stä mikäli A:n instanssit kutsuvat B:n instanssien metodeja A:n instanssit sisältävät viitteitä B:n instansseihin Perintäsuhteet ja abstraktit luokat voidaan jättää huomioimatta Huom! call-back tyyppisten kutsujen tapauksessa (esim. käyttöliittymäkirjastot) sovellus riippuu aina kirjastosta eikä päinvastoin Ohjelmistojen testaus, 2013 19(32)
Luokkien integrointitestaus 2/3 Hyvin suunnitelluissa ohjelmistoissa riippuvuudet eivät yleensä muodosta silmukoita, jolloin klusterin luokat ja niiden väliset riippuvuudet muodostavat puumaisen rakenteen Mikäli silmukoita löytyy, ne tulee purkaa korvaamalla jokin luokka tyngällä Aloitetaan testaus klusterin lehtiluokista, jotka eivät riipu muista luokista Liitetään mukaan lehtiluokkien kantaluokkia jne. edeten kohti puun juurta Ohjelmistojen testaus, 2013 20(32)
Luokkien integrointitestaus 3/3 Jokaisessa vaiheessa suunnitellaan/ajetaan klusterille sen toiminnallisuutta testaavia mustalaatikkotestejä Tarvittaessa suunnitellaan/ajetaan lisäksi lasilaatikkotestejä Esim. kunnes tavoitteeksi asetettu kattavuus on saavutettu Testitapauksien pitäisi testata erityisesti myös Sellaisia poikkeuksia, jotka heitetään eri luokassa kuin missä ne käsitellään (klusterin sisällä) Klusterin luokkien välillä tapahtuvia polymorfisia metodikutsuja Ohjelmistojen testaus, 2013 21(32)
Oliotestauksen skaalaaminen riskien suhteen 1/3 Pöyhösen ja Stenbergin (NRC) mukaan (www.cs.tut.fi/tapahtumat/olio2002/): Pieni riski: 100% metodikattavuus ekvivalenssiluokkien käyttö (lailliset ekvivalenssiluokat) raja-arvoanalyysin käyttö (laillisten ekvivalenssiluokkien raja-arvot) Keskeisten perintäsuhteiden testaus Dynaaminen sitominen testataan perinnän yhteydessä Muistinhallinnan testauksessa testataan tärkeimmät rakentajat sekä kaikki purkajat Joitakin osia koodista tarkastetaan Käytetään koodin staattista analysointia Oliomallit tarkastetaan / katselmoidaan epämuodollisesti Ohjelmistojen testaus, 2013 22(32)
Oliotestauksen skaalaaminen riskien suhteen 2/3 Keskisuuren riskin tapauksessa edellisten lisäksi: 100% metodikattavuus ekvivalenssiluokkien käyttö (lailliset + laittomat ekvivalenssiluokat) Enemmän perintäsuhteita Poikkeuksien testaus Dynaaminen sidonta testataan erikseen Ei kuitenkaan kaikkia kombinaatioita Kaikki rakentajat testataan Oliomallit tarkastetaan Ohjelmistojen testaus, 2013 23(32)
Oliotestauksen skaalaaminen riskien suhteen 3/3 Suuren riskin tapauksessa edellisten lisäksi: 100% metodikattavuus raja-arvoanalyysin käyttö (laillisten + laittomien ekvivalenssiluokkien raja-arvot ) Kaikki perintäsuhteet testataan Dynaamisen sidonnan kaikki kombinaatiot testataan Järjestelmälliset tarkastukset koodille Ohjelmistojen testaus, 2013 24(32)
Mock-oliot 1/7 Olioiden yksikkötestauksessa havaittuja käytännön ongelmia: Riippuvuuksien korvaaminen tyngillä voi olla työlästä (tietokannat, protokollapinot, käyttöliittymät) Testattavan yksikön koko kasvaa Testien toistettavuus usein heikko Ad-hoc tyyppistä käsityötä Järjestelmää ei ole suunniteltu testattavaksi Mackinnon, Freeman, Craig: Endo-testing: Unit testing with Mock Objects, XP2000 Mock-olioita voidaan ajatella älykkäinä tynkinä Ohjelmistojen testaus, 2013 25(32)
Mock-oliot 2/7 Strukturoidumpi tapa luoda ja ohjata alkuperäisiä palveluita korvaavaa testikoodia Vaatii sovelluskohteelta hyvien tapojen mukaista oliosuunnittelua Mock-olioiden käyttö voi vaatia refaktorointia Mock-olioiden edut Halutun tilan asettaminen helppoa Toiminnallisuuksien muuttaminen helppoa Tilan tarkastaminen helppoa Ohjelmistojen testaus, 2013 26(32)
Mock-oliot 3/7 Esimerkki: testataan yksinkertaista valuuttamuunninta: public class Converter_math { }}} public static void Eur2fim(String euroja_str, PrintStream out){ double markkoja_d = 0.0; try { markkoja_d = Double.parseDouble(euroja_str)*5.94573; out.println("tulos: " + euroja_str + " euroa vastaa " + markkoja_d + " markkaa."); } catch (NumberFormatException e) { out.println("virheellinen euromäärä: " + euroja_str); Ohjelmistojen testaus, 2013 27(32)
Mock-oliot 4/7 Pääohjelma: public class Converter_main { }}} public static void main(string[] args) { System.out); if(args.length >= 1) { }} if(args[0].equals("testaus")) { test_converter_math(); } else { else { Converter_math.Eur2fim(args[0], System.err.println("Käyttö: Anna " + "konvertoitava euromäärä parametrina"); Ohjelmistojen testaus, 2013 28(32)
Mock-oliot 5/7 Testaus hyödyntäen mock objecteja: public static void test_converter_math() { MockPrintStream mock = new MockPrintStream(System.out); // Testitapaus 1 mock.setexpectedprintlncalls(1); mock.setexpectedstringsegment("virheellinen euromäärä: " + "virhetilanne"); Converter_math.Eur2fim("virhetilanne", mock); mock.verify(); // Testitapaus 2 mock.setexpectedprintlncalls(1); mock.setexpectedstringsegment("tulos: 100 euroa vastaa + 594.573 " + "markkaa"); Converter_math.Eur2fim("100", mock); mock.verify(); } Ohjelmistojen testaus, 2013 29(32)
Mock-oliot 6/7 Sovelluskehyksiä/kirjastoja, jotka tarjoavat erilaisia tapoja ja ympäristöjä käyttää mock-olioita: POCMock/NMock (.NET) Mockrunner (J2EE) EasyMock DynaMock Ohjelmistojen testaus, 2013 30(32)
Mock-oliot 7/7 Kehittäjällä vastuu testattavuuden rakentamisesta Suunnittelumallit auttavat Vastuuta ei voi siirtää työkaluille tai menetelmille Yksikkötestaus vaatii hikeä/asennemuutosta, varsinkin alussa kun testiympäristö täytyy pystyttää Mock-olioiden käytöstä voi seurata se, että testattava yksikkö suunnitellaan paremmin Ohjelmistojen testaus, 2013 31(32)
Testauspatternit Viimevuosina on tunnistettu testauspatterneja, joita voi hyödyntää testauksessa samaan tapaan kuin suunnittelumalleja (design pattern) oliopohjaisessa suunnittelussa Esimerkkinä mainittakoon self-shunt-yksikkötestauspatterni Ideana on, että testitapausta mallintava olio välittää itsensä parametrina testikohteelle teeskennellen olevansa jokin oikea olio, jonka kanssa testikohteen pitäisi kommunikoida Ohjelmistojen testaus, 2013 32(32)