- TIE-0240x
- 5. Modulaarisuus: periytyminen ja abstraktit kantaluokat
- 5.4 Harjoitus (PAKOLLINEN): (Yksikkö)testaus ja CI
Harjoitus (PAKOLLINEN): (Yksikkö)testaus ja CI¶
Tämän viikon tavoitteena on harjoitella QtTest-yksikkötestauskehykseen ja sen avulla harjoitella annetun luokan yksikkötestausta. Tämän ohella tutustutaan kurssin CI-ympäristön käyttöön. Huomaa, että vaikka harjoitus voi tuntua pitkältä, voit keskittyä yhteen asiaan kerrallaan.
Harjoituksen pakollisena osana teet kohdat Testiprojekti ja GitLabCI. Palautus tehdään versionhallinnan avulla. Muista tehdä commit riittävän usein!
Tehtävän vaatimat tiedostot saat nyhtämällä versionhallinnasta. Ne sijaitsevat repositoriossa student_template_project
https://course-gitlab.tuni.fi/tie-0240x-ohjelmointi-3-programming-3_independent_2021/student_template_project.
Se tulee laittaa paikalliseen repositorioosi remoteksi. Tässä harjoituksessa tarvitsemasi koodit ovat hakemistossa EX3. Huomaa, sinulla ei ole oikeuksia päivittää repositoriota.
.gitlab-ci.yml
: koko repositorion juuressa. yaml-tiedosto, jossa CI/CD-konfiguraatioEX3
:WelcomeToTampere
: alkutekijöissään oleva ohjelmistoprojektiWelcomeToTampere.pro
: projektin projektitiedostobaddate.cc
,baddate.hh
,date.cc
,date.hh
,main.cc
,morottaja.cc
,morottaja.hh
: kooditiedostot
UnitTest
: projektin yksikkötestiprojektiUnitTests.pro
: yksikkötestiprojektin projektitiedostoMorottaja
: morottaja-komponentin testikoodiMorottaja.pro
: yksikkötestiprojekti projektitiedostomorottajatest.cc
: testikoodi morottaja-komponentille
Huom! Testattavassa luokassa Date
saattaa olla bugeja (joko koodissa tai rajapinnan dokumentaatiossa/suunnittelussa). Näiden bugien korjaaminen ei kuitenkaan ole tämän harjoituksen tarkoitus.
Testiprojekti¶
Lisää UnitTest
iin projekti Date
päivämääräluokan yksikkötestausta varten right click->New Subproject-> Other Project-> Auto Test Project
. Testiprojektiksi valitse Qt Test
. Valitse myös Requires QApplication
sekä luonti- ja siivouskoodin generointi. Huom! yksikkötesteillä ei ole otsikkotiedostoja. Niistä puhuttaessa viitataan testattavan koodin otsikkotiedostoihin.
Laita lähdekooditiedostot mukaan yksikkötestiprojektiin lisäämällä niistä tieto Date.pro
-tiedostoon.
SOURCES +=
HEADERS +=
Jotta testeissä tarvittavat tiedostot löytyvät käännettäessä, lisää tiedostoon vielä
INCLUDEPATH +=
DEPENDPATH +=
Katso mallia Morottaja.pro
tiedostosta.
Projektin kääntäminen ja ajaminen¶
Kun käännät (build) ylimmän tason projektin, QtCreator kääntää sekä itse "sovelluksen" että yksikkötestit. Valitsemalla aliprojektin ja sieltä ajamisen (run), ajaa QtCreator valitusta aliprojektista riippuen joko itse "sovelluksen" tai yksikkötestit.
Yksikkötestien kirjoittaminen¶
Qt:n yksikkötestikehyksessä jokainen testitapaus kirjoitetaan testiluokkaan omaksi jäsenfunktiokseen. Testikehys kutsuu näitä jäsenfunktioita automaattisesti järjestyksessä. (Qt:n ohjesivu testauskehykseen löytyy täältä .)
Yksikkötestauksessa idea on, että jokainen testitapaus testaa yhtä tiettyä luokan ominaisuutta tai jäsenfunktiota (monimutkaisempien jäsenfunktioiden testaus kannattaa jakaa useaan testitapaukseen). Hyöty useaan tapaukseen jakamisessa on, että virheen löytyessä testiajon tulostuksesta näkee tarkemmin, missä virhe sattui. Lisäksi testikehys toimii niin, että jokainen yksittäinen testitapaus keskeytyy ensimmäiseen siinä huomattuun virheeseen (eikä kyseisessä jäsenfunktiossa olevia mahdollisia muita testejä ajeta), mutta itse testiajo jatkuu kuitenkin eli loppujakin testijäsenfunktioita kutsutaan.
Testitapausjäsenfunktioiden kirjoittamisessa kannattaa taas käyttää hyväkseen QtCreatorin apuja. Kirjoita jäsenfunktion esittely luokan esittelyyn (harjoituksessa luokka DateTest
) ja sitten tuota automaattisesti jäsenfunktion runko valitsemalla oikea nappi > Refactor > Add definition outside class
.
Testitapausten kirjoittaminen¶
Hyvien testitapausten valinnassa on hyvä tilaisuus käyttää mielikuvitustaan ja luovuuttaan (tietysti rautaisella analyysilla maustettuna). Testattavassa luokassa ei ole kylvettynä tässä vaiheessa tarkoituksellisia bugeja, mutta koska sitä ei ole koskaan testattu kattavasti, on lähes varmaan että bugeja löytyy... :-)
Testitapauksia kannattaa usein kirjoittaa dataohjatusti. Testiluokassa on esimerkkinä muutama testitapaus havainnollistamassa kehyksen käyttöä. Lyhyesti ideana on siis ensin luoda päiväysolio, kutsua sille testattavaa operaatiota ja sitten varmistua siitä, että operaation tulos on haluttu. Tämä testaus tehdään testikehyksen omilla testauskomennoilla (QCOMPARE
, QVERIFY
, QFAIL
), jotta virheet saadaan kirjattua. Testausmakrojen dokumentaatio löytyy täältä.
Jos samaa testitapausta halutaan ajaa usealla eri testidatalla (tyypillistä), testikehys antaa tähän mahdollisuuden. Tällöin itse testitapaus kirjoitetaan edelleen omaksi jäsenfunktiokseen. Tämän lisäksi kirjoitetaan toinen jäsenfunktio päätteellä _data
, ja tämä jäsenfunktio tuottaa testimatriisin, jossa halutut testitapaukset ovat. Tästä on esimerkkinä alla valmis testitapaus viikonpaiva
. Jäsenfunktio viikonpaiva_data
ensin määrittää testitapauksen "syötteiden" tyypit ja nimet (QTest::addColumn
), ja sen jälkeen luo testimatriisin, jossa jokainen rivi on oma nimetty testitapauksensa (QTest::newRow
). Varsinaista testijäsenfunktiota viikonpaiva kutsutaan automaattisesti jokaiselle testimatriisin riville. Jäsenfunktio lukee alussa testimatriisin syötedatan (QFETCH
) ja sitten suorittaa normaalin testin. Huomaa, että QFETCH
luo automaattisesti testisyötteen nimisen muuttujan jäsenfunktioon.
void Unittest::weekday()
{
// This method tests a row of the test matrix generated by the method weekday_data.
// It is called automatically for each row of the matrix
// Fetching data from the matrix, variables are created automatically
QFETCH(unsigned int, day);
QFETCH(unsigned int, month);
QFETCH(unsigned int, year);
QFETCH(Date::Weekday, weekday);
// Performing the test
Date d(day, month, year);
QVERIFY2(d.giveWeekday() == weekday, "Wrong weekday");
}
void Unittest::weekday_data()
{
// This method defines the test matrix for the weekday test and generates the desired test cases there
// Defining columns for the test matrix (types and names)
QTest::addColumn<unsigned int>("day");
QTest::addColumn<unsigned int>("month");
QTest::addColumn<unsigned int>("year");
QTest::addColumn<Date::Weekday>("weekday");
// Generating test cases for the test matrix, 3u etc. are needed since the type is unsigned
QTest::newRow("today") << 3u << 2u << 2014u << Date::MONDAY;
QTest::newRow("last Christmas") << 24u << 12u << 2013u << Date::TUESDAY;
QTest::newRow("next May Day") << 1u << 5u << 2014u << Date::THURSDAY;
QTest::newRow("end of the year")<< 31u << 12u << 2013u << Date::TUESDAY;
QTest::newRow("new year") << 1u << 1u << 2014u << Date::WEDNESDAY;
}
Jotta dataohjattuja testitapauksia voi tehdä, jossain kohtaa lähdekooditiedostoa tulee olla määriteltynä Q_DECLARE_METATYPE(Date::Weekday);
. Tämän testin lisäksi ainakin yksi lisätesti tulee kirjoittaa.
GitLabCI¶
Tietovaraston juuressa on tiedosto .gitlab-ci.yml
. Sen tehtävänä on konfiguroida GitLabCI:n toimintaa. Lisätietoa CI/CD:stä sekä
.gitlab-ci.yml
:n konfiguroinnista löydät helpistä .
Putken konfiguroinnista¶
Putken konfiguroinnin keskeinen elementti on ns. jobi (engl. job). Putkessa voi olla useita jobeja. Jobit
- ovat ylimmän tason käsitteitä putken sisällöstä
- ovat nimeltään mielivaltaisia
- sisältävät aina vähintään
script
lausekkeen - määritellessä määritellään myös rajoitteet, joiden puitteissa ne ajetaan
- lukumäärää ei ole rajoitettu
Esimerkki jobista:
build_EX1:
script:
- cd EX1/DesignByContract
- qmake
- make
- make clean
Jobia määritellessä käytetään joukkoa parametreja, jotka määrittävät, miten jobi käyttäytyy. Keskeisimmät:
tags
: käytetään määrittämään, millä ns. runnerilla jobi ajetaan GitLabissa. Tällä kurssilla ainaqt
.image
: kertoo, mitä Docker imagea jobin ajamisessa käytetään. Tällä kurssilla määritelty valmiiksi.stage
: kertoo mihin pipelinen vaiheeseen jobi liittyy. Nämä määritellään koko konfiguraatiolle. Tässä tehtävässä staget ovatbuild
jatest
nimiään vastaaviin tehtäviin.artifacts
: määrittelee, mitkä tiedostot ja kansiot, jotka halutaan pitää tallessa seuraaville jobeille tai esim. tarkastelua varten. Voidaan määritellä säästettävän materiaalin polunpaths:
lisäksi mm.expire_in
tallennetun materiaalin poistamiseksi tietyn ajanjakson jälkeendependencies
: oletuksena kaikki tallessa pidetyt tiedostot ja kansiot välitetään jobeille, muttadependencies
avulla voidaan listata ne jobit, joiden materiaali halutaan jobille välittää. Kätevä kääännös- ja testijobien välillä.
Daten testien CI¶
Avaa tiedosto editoidaksesi sitä. Tavoitteena on lisätä testien ajaminen osaksi CI-putkea. Huomaat, että tällä hetkellä mukana putkessa on mukana vaiheet viikkoharjoitusten käännöksille sekä Morottajan yksikkötesteille. Tutustu tiedostoon ja lisää sinne Daten yksikkötestit.
Tiedostosi syntaksin oikeellisuuden voit tarkistaa GitLab CI:n linterillä https://course-gitlab.tuni.fi/tie-0240x-ohjelmointi-3-programming-3_independent_2021/reposi_nimi/-/ci/lint , jossa reposi_nimi
vaihdetaan oman työvarastosi pelkkä nimi. Linter on työkalu, joka tarkistaa määrämuotoisesta tiedostosta (esim. yaml, kooditiedosto), että se noudattaa formaatin vaatimaa syntaksia.
Palautus¶
Työ palautetaan viime viikolta tuttuun tapaan tagaamalla. Tagin pitää alkaa EX3_submission.
A+ esittää tässä kohdassa tehtävän palautuslomakkeen.