Yksikkötestaus , joskus yksikkötestaus tai yksikkötestaus ( eng. unit testing ) on ohjelmoinnin prosessi, jonka avulla voit tarkistaa ohjelman lähdekoodin yksittäisten moduulien , yhden tai useamman ohjelmamoduulin joukon sekä vastaavien ohjaustietojen oikeellisuuden, käyttö- ja käsittelymenettelyt.
Ajatuksena on kirjoittaa testejä jokaiselle ei-triviaalille funktiolle tai menetelmälle. Tämän avulla voit nopeasti tarkistaa, onko seuraava koodimuutos johtanut regressioon , eli virheiden ilmaantumiseen ohjelman jo testatuissa paikoissa, ja helpottaa myös tällaisten virheiden havaitsemista ja poistamista. Voit esimerkiksi päivittää projektissa käytetyn kirjaston nykyiseen versioon milloin tahansa suorittamalla testejä ja tunnistamalla yhteensopimattomuudet.
Yksikkötestauksen tavoitteena on eristää ohjelman yksittäiset osat ja osoittaa, että osat toimivat erikseen.
Tämän tyyppisen testauksen tekevät yleensä ohjelmoijat .
Myöhemmin suoritetun yksikkötestauksen avulla ohjelmoijat voivat reagoida uudelleen ja olla varmoja siitä, että yksikkö toimii edelleen oikein ( regressiotestaus ). Tämä rohkaisee ohjelmoijia vaihtamaan koodia, koska on tarpeeksi helppoa tarkistaa, että koodi toimii edelleen muutoksen jälkeen.
Yksikkötestaus auttaa poistamaan yksittäisiä moduuleja koskevat epäilykset, ja sitä voidaan käyttää alhaalta ylöspäin suuntautuvaan testaukseen: ensin testataan yksittäisiä ohjelman osia ja sitten koko ohjelmaa.
Yksikkötestejä voidaan pitää "elävänä asiakirjana" testattavalle luokalle . Asiakkaat, jotka eivät osaa käyttää tätä luokkaa, voivat käyttää yksikkötestiä esimerkkinä.
Koska jotkin luokat voivat käyttää muita luokkia, yhden luokan testaus ulottuu usein toisiinsa liittyviin luokkiin. Esimerkiksi luokka käyttää tietokantaa; kirjoittaessaan testiä ohjelmoija huomaa, että testin on oltava vuorovaikutuksessa tietokannan kanssa. Tämä on virhe, koska testi ei saa mennä luokkarajan ulkopuolelle. Tämän seurauksena kehittäjä abstraktioi tietokantayhteyden ja toteuttaa tämän rajapinnan käyttämällä omaa valeobjektiaan . Tämä johtaa vähemmän yhtenäiseen koodiin, mikä minimoi järjestelmän riippuvuudet.
Ohjelmistojen testaus on kombinatorinen tehtävä. Esimerkiksi Boolen muuttujan jokainen mahdollinen arvo vaatisi kaksi testiä, yksi TOSI ja toinen EPÄTOSI. Tämän seurauksena jokainen lähdekoodirivi vaatii 3–5 riviä testikoodia.
Algoritmeilla, kuten marssikuutioilla tai punamustalla puulla , on haarautunut päätöspuu, ja kaikkien vaihtoehtojen tarkistamiseen tarvitaan valtavia testisarjoja: yhdessä GitHubin punamusta puutoteutuksesta tehtiin kaksitoista testiä lisäyksen tarkistamiseksi [1] . Toisessa he rakentavat automaattisesti 10! = 3,6 miljoonaa permutaatiota ja koe ne kaikki [2] .
Kuten mikä tahansa testaustekniikka, yksikkötestaus ei salli kaikkia ohjelmavirheitä. Itse asiassa tämä johtuu siitä, että käytännössä on mahdotonta jäljittää kaikkia mahdollisia ohjelman suorituspolkuja, paitsi yksinkertaisimpia tapauksia.
Esimerkiksi matemaattisessa mallintamisessa . Yrityssovellukset toimivat usein äärellisten ja laskettavien joukkojen kanssa, kun taas tieteelliset sovellukset jatkuvat . [3] Siksi on vaikeaa valita testejä kullekin ohjelmahaaralle, on vaikea sanoa, onko tulos oikea, säilyykö tarkkuus jne. Ja monissa tapauksissa mallinnuksen laatu määräytyy "silmällä". ”, ja viimeinen tulos tallennetaan ”viittaukseksi”. Jos poikkeama löytyy, uusi tulos tarkistetaan manuaalisesti ja selvitetään kumpi on parempi: vanha vai uusi.
Koodia, joka on vuorovaikutuksessa porttien , ajastimien , käyttäjän ja muiden järjestelmän "epävakaiden" osien kanssa, on erittäin vaikea testata eristetyssä ympäristössä.
Mutta tämä ei tarkoita, että yksikkötestaus olisi täysin sopimaton tähän: se pakottaa ohjelmoijan siirtymään esimerkiksi tiedostoista ja porteista abstrakteihin virtoihin . Tämä tekee koodista yleisemmän (esimerkiksi voit vaihtaa tiedostoista verkkopistorasioihin ilman ongelmia ), testattavamman (voit tarkistaa "yhteys katkennut" -tilanteen kirjoittamalla streamin, joka simuloi onnettomuutta N tavun jälkeen; Tarkista Windows-osassa Unix- polun
Se on pohjimmiltaan epävakaa osa järjestelmää. Lisäksi yksikkötestit ovat yleensä yksinkertaisia, kun taas monisäikeisten järjestelmien testien pitäisi päinvastoin olla melko suuria.
Yksikkötestejä suoritettaessa jokainen moduuli testataan erikseen. Tämä tarkoittaa, että integrointivirheitä, järjestelmätason virheitä tai useissa moduuleissa suoritettuja toimintoja ei havaita. Lisäksi tämä tekniikka on hyödytön suorituskykytesteissä. Siten yksikkötestaus on tehokkaampaa, kun sitä käytetään yhdessä muiden testaustekniikoiden kanssa.
Yksikkötestauksen hyötyjen hyödyntäminen edellyttää tiukkaa testausteknologian noudattamista koko ohjelmistokehitysprosessin ajan. On välttämätöntä pitää kirjaa kaikista suoritetuista testeistä, mutta myös kaikista lähdekoodin muutoksista kaikissa moduuleissa. Tätä tarkoitusta varten tulisi käyttää ohjelmistoversionhallintajärjestelmää . Näin ollen, jos ohjelmiston myöhempi versio epäonnistuu aiemmin onnistuneesti läpäistyssä testissä, on helppo tarkistaa lähdekoodin muunnelmat ja korjata virhe. Sinun on myös varmistettava, että epäonnistuneita testejä seurataan ja analysoidaan jatkuvasti. Tämän vaatimuksen huomiotta jättäminen johtaa epäonnistuneiden testitulosten vyöryyn.
Yksinkertaisimpia tapauksia lukuun ottamatta testattavan kohteen on oltava vuorovaikutuksessa muiden objektien kanssa. Nämä "yhteistyökumppanit" - tynkäobjektit - on tehty erittäin yksinkertaisiksi: joko äärimmäisen yksinkertaistettuja (muisti tietokannan sijaan) tai suunniteltu tiettyä testiä varten ja mekaanisesti toistava vaihto-istunto. Ongelmia voi syntyä vaihdettaessa vaihtoprotokollaa, jolloin tynkäobjektien on täytettävä uusia protokollavaatimuksia. [neljä]
On helppo varmistaa, että moduuli toimii kehittäjän koneessa. Vaikeampi - että kohdekoneessa, usein hyvin rajoitettu [5] .
Äärimmäinen ohjelmointi olettaa yhtenä oletuksena automaattisten yksikkötestaustyökalujen käytön. Tämän työkalupakin voi luoda joko kolmas osapuoli (kuten Boost.Test) tai sovelluksen kehitystiimi.
Extreme ohjelmointi käyttää yksikkötestejä testilähtöiseen kehitykseen . Tätä varten kehittäjä kirjoittaa ennen koodin kirjoittamista testin, joka vastaa moduulin vaatimuksia. On selvää, että testin ennen koodin kirjoittamista ei pitäisi toimia. Jatkoprosessi rajoittuu lyhimmän tämän testin täyttävän koodin kirjoittamiseen. Kun kehittäjä on kirjoittanut seuraavan testin, koodin ja niin edelleen monta kertaa.
Kirjoitusyksikkötestien monimutkaisuus riippuu siitä, kuinka koodi on järjestetty. Yksittäisten entiteettien (oliosuuntautuneiden kielten luokat) vahva koheesio tai suuri vastuualue voi tehdä testaamisesta vaikeaa. Stubit tulee luoda objekteille, jotka kommunikoivat ulkomaailman kanssa (verkko, tiedostojen I/O jne.). Terminologiassa erotetaan "kehittyneemmät" tyngät - logiikkaa kantavat valeobjektit . Se on myös helpompi testata erottamalla mahdollisimman suuri osa logiikasta puhtaiksi funktioiksi . Ne eivät ole vuorovaikutuksessa ulkomaailman kanssa millään tavalla ja niiden tulos riippuu vain syöteparametreista.
On tapana erottaa testikoodi erillisiin hakemistoihin. On toivottavaa, että uusien testien lisääminen projektiin ei ole vaikea tehtävä ja että kaikki testit on mahdollista suorittaa. Jotkut versionhallintajärjestelmät, kuten git, tukevat koukkuja ( englanniksi hook ), joilla voit määrittää kaikkien testien käynnistyksen ennen muutosten tekemistä. Jos ainakin yksi testeistä epäonnistuu, muutoksia ei hyväksytä. Myös jatkuvan integroinnin järjestelmiä voidaan soveltaa .
Suosituimpia korkean tason ohjelmointikieliä varten on yksikkötestaustyökaluja ja kirjastoja. Jotkut heistä:
Joillakin kielillä on tuki yksikkötestaukselle syntaksitasolla. Tämä poistaa tarpeen valita, mihin kehykseen linkittää, ja helpottaa koodin siirtämistä muihin projekteihin.
Esimerkki tällaisista kielistä:
Koodiesimerkki D -kielellä
luokka ABC { this () { val = 2 ; } yksityinen int val ; julkinen funktio () { val *= 2 ; } } yksikkötesti { ABC a ; a . func (); väittää ( a . val > 0 && a . val < 555 ); // voit käyttää yksityistä muuttujaa moduulin sisällä }