ComTest ja Ohjelmointi 2 -kurssi
ComTest on lähtöisin alun perin Vesa Lappalaisen ideasta, että yksikkötestauksen pitäisi olla mahdollisimman helppoa ja vaivatonta. Projekti on tällä hetkellä siinä vaiheessa, että Eclipse Pluginilla voidaan luoda ja ajaa kohtuullisella vaivalla testejä. Eclipse ei kuitenkaan tällä hetkellä ymmärrä testien syntaksia ja tämä hieman vaikeuttaa testien kirjoittamista. Ennen pluginin suurempaa hiomista pitää kuitenkin varmistaa perusajatuksen toimivuus.
Tärkeää ComTestissä on yksikkötestaamisen lisäksi se, että siitä saa myös hyvän dokumentaation metodien toiminnasta (ks. Mjonot-dokumentaatio). Lisäksi helppokäyttöisten testien avulla voi mukavasti määritellä itselleen esimerkiksi metodin rajapinnan.
ComTest on "vain" makrokieli, jolla kirjoitetut testit "käännetään" Javan normaaliksi JUnit-testitiedostoksi. Alempana on taulukkomuotoisen testaamisen yhteydessä esimerkkejä, joista voi katsoa minkälaiseksi JUnit-tiedostoksi ComTest-testi kääntyy.
Mjonot -linkki sanoo 404
VL: Vaihdoin linkin uudempaan.
—Lisenssiasiaa: jos projektin haluaa julkaista omalla nimellään ja siellä on käyteytty ComTestiä tai muita kirjastoja, ja ylipäätään projektia on tehty tällä kurssilla, miten laki- ja lisenssiasiat menee? Käsittääkseni itse tehty työ on täälläkin aina oma ja kurssin erikoisemmat työkalut kuten comtest lienevät MIT-lisenssin alla. Jos lopulta ohjelmasta tulee vaikka maksullinen, tuleeko mitään ongelmia? Omistanhan täysin itse projektin jota on tehty tällä kurssilla?
VL: Vaikka sitä ei ole nähtävästi mihinkään erikseen kirjoitettu, niin ainakin mun juttujen oletetaan olevan MIT-lisenssillä (kuten TIM), eli saa käyttää. Toki omanaan myyminen on epäreilua jos ei ole merkittävää omaa kontribuutiota. ELi oman projektisi omistat ja saat käyttää kurssin materiaaleja/aliohjelmia sen toteuttamiseksi.
—1. Käyttöesimerkki
- IntelliJ IDEA:aan on omat ComTest asennusohjeet
2. Asentaminen kotikoneeseen (Eclipse)
- Asenna kotikoneeseen eli käytännössä tallenna comtestplug_1.0.13.jar Eclipse
dropins
-kansioon:
Win: c:\devel\ec2409\eclipse\dropins
Mac: /Applications/Eclipse.app/Contents/Eclipse/dropins
Linux: /home/Anonymous/devel/ec2409/Eclipse/dropins
- Mene Safarissa linkin päälle ja paina kahdella sormalle
- Ota
Lataa linkitetty tiedosto
- Avaa kaksi Finder ikkunaan vierekkäin
- Toiseen lataukset
- Toiseen tuo Eclipse ja sen kohdalla kahdella sormella
Näytä pakkauksen sisältö
- Jatka
dropins
-kansioon saakka - Raahaa
Lataukset
-ikkunastadropins
-kansioon - Käynnistä uudelleen Eclipse.
- Ilman mitään asentamista, voit kokeilla ComTestin toimintaa Copy/Pasteamalla koko testattava ohjelma (luokka) TIM-oppimisympäristön ComTest-pohjaan.
2.1 Mistä tiedän että toimii?
Tee uusi Eclipse Java-luokka (jos ei ole projektia, niin tee ensin uusi projekti):
File/New/Class
- Package:
example
- Name:
Laskuja
Finish
Kopioi tiedoston
Laskuja
sisällöksi:package example; /** Kokeillaan testejä */ public class Laskuja { /** * @param a eka summattava * @param b toka summattava * @return a+b * @example * <pre name="test"> * summa(2,3) === 5; * summa(-5,5) === 0; * </pre> */ public static int summa(int a, int b) { return a + b; } /** @param args ei käytössä */ public static void main(String[] args) { System.out.println("Summa = " + summa(3,2)); } }
Lisää projektiin
Junit 5
:- Paina projektin nimen päällä hiiren oikealla
- Valite
Properties
Java Build Path/Libraries
- Klikkaa
Classpath
(jos Classpath ei ole valmiiksi näkyvissä, klikkaa ensin Libraries.) Add Library...
Junit
Next
JUnit 5
Finish
Apply and Close
Valitse tiedosto
Laskuja.java
, sitten hiiren oikeallaComTest/Generate, Run Junit
Pitäisi tulla vihreää.
Siirrä testi-ikkuna alalaitaan
Console
-ikkunan kaveriksiPoista syntynyt testitiedosto
LaskujaTest.java
Lisää alempana olevan ohjeen mukaan
comtest.ini
esim omaanohj2
-kansioosi niin jatkossa testit tulevat omaan hakemistoonsa.Kokeile vielä tämän jälkeen ajaa testit uudelleen
Laskuja
-tiedostolle.Jos
Qtrl-Q
ei toimi, niin:- Window/Preferences
- kirjoita hakusanaksi
keys
- Klikkaa
Keys
- Kirjoita
Scheme
alapuolella olevaan hakusana-kohtaanLast edit
taiPrevious edit location
- Jos
Ctrl-Q
on yhdistetty tähän, niin mene kohtaanBinding
ja tyhjennä se. - Paina
Apply
. - Kirjoita vielä hakusanaksi
comtest
ja katso onko silläCtrl-Q
jaWhen:
-kohdassaEditing Text
- Muista että aina ennen uutta testiä pitää tallentaa (
Ctrl-S
)
Miksi antaa comtestistä “This chosen operation is not currently available”?
VL: toistaiseksi kaikilla joilla on ollut tuo vika, on ollut useita Javoja koneessa ja Eclipse käyttää oletuksena jotakin vanhaa. Helpoin tapa on poistaa kaikki Javat ja sitten tehdä JDK 13 asennus uudelleen niin, että on vain yksi Java.
Tuolta löytyi ohje miten saa Eclipsen käyttämään oikeaa jdk:ta. Laitoin myös ohjeen virhesivulle, ainakin itsellä korjautui. - pyjuella
ComTest: Saan ilmoituksen “No JUnit tests found” ja en saa ratkaistua ongelmaa, miksi ei toimi. Junit5 on laitettu eclipseen.
VL: Tuo tulee silloin kun testattavassa ohjelmassa ei ole ComTestin vaatimia otiskoita kuten example
ja pre
. Voinee tulla myös jos ComTest.jar ei löydy tai Javaa ei voi ajaa?
Itsellä ei ComTestit toiminut kun lisäsin Junit 5 kirjaston. Kun lisäsin kirjastoon myös Junit 4 & 3 niin sain ohjeissa olevan Comtest/Generate, Run Junit vaihtoehdon. Jos muilla vastaava ongelma niin suosittelen testaamaan.
—ilmoitus: no JUnit test found kohdan 4 jälkeen…
HL: Kohdassa 3 lisätään projektiin Junit 5
.
Minullakin tulee, että "no junit tests found". Siihen alle kyllä tulee kaksi uusi haara "example test" ja sen alle LaskujaTest.java. Mainittakoon vielä ylemmästä HL:n ohjeesta, että JUnit 5 on lisätty. :SOLVED
VL: Liatatko vielä mikä oli vika jos muilla on samaa?
—- Kokeile vielä että ComTest-pohjan teko onnistuu (unohtui videolla):
- Tee kommentin sisällä (eli
/**
ja*/
välissä) vaikka juuri ennen loppumerkkiä uusi rivi. - Kirjoita tähden perään (ei kiinni tähteen, vaan ainakin yksi välilyönti) comt ja paina Ctrl-Space (eli Ctrl ja välilyönti).
- Pitäisi ilmestyä runko ComTestin kirjoittamiselle. Tosin tuonne on ilmestynyt valtava määrä muitakin ko lyhenteellä tulevia ja ComTest on niiden viimeisenä. Se löytyy nopeasti, kun painaisee nuoli ylös. Itse asiassa riittää:
c Ctrl+väli nuoli ylös
- Jos haluat valtavan listan karsia pois, niin mene valikossa kohtaan Window -> Preferences -> Java -> Editor -> Content Assist -> Advanced ja sieltä ylemmästä laatikosta (Select the proposal kinds contained in the 'default' content assist list) ota ruksi pois kohdasta Java proposals ja paina Apply.
- Tee kommentin sisällä (eli
Hätätilassa lue tarkemmin ComTestin asennusohjeesta.
3. Asentaminen mikroluokassa
- Tarkista ensin toimiiko ComTest: Java tiedoston päällä paina hiiren oikeata nappia
- Jos ComTest näkyy, niin asia on kunnossa.
- Jos ComTest ei näy, niin toimi seuraavasti
Eclipse 3.4 ja uudemmat
- siirry komentoriville
- kirjoita alustacomtest (jos n-levy ei ole käytössä, aktivoi se työpöydältä)
4. Ongelmatapauksissa
- Ongelmatapauksissa katso ensin: ComTestin ongelmia-kohdasta
- Mainittu Eclipsen configuraatiohakemiston on mikroluokissa
u:\config\eclipse
(eli jos tulee suuria ongelmia, sammuta Eclipse, poista tämä hakemisto, käynnistä Eclipse uudelleen)
5. Peruskäyttö
Testien hyvän toiminnan kannalta Java-tiedostot kannattaa kirjoittaa paketteihin (ei default package, aloita siis aina luomalla src-hakemiston alle työtäsi kuvaava paketti)
ÄLÄ käytä tiedostojen ja pakettien yms. nimissä SKANDEJA (ä,ö,å). Tulee siis ongelmia jos pakettisi nimi on
tehtäviä
, vaihda siis tällaiset nimet muotoontehtavia
Nykyään tämä tehdään automaattisesti. Jos haluat testien menevän eri hakemistoon kuin itse Java-tiedostojen (isoissa projekteissa tapana), kirjoita jollakin tekstieditorilla tiedosto
ComTest.ini
. Sijoita se sinne hakemistoon, missä on testatta Java-tiedostosi tai johonkin sen yläpuoliseen hakemistoon. TiedostonComTest.ini
jonka sisältö on oletusarvoisesti:#BEFORETESTCLASS= @SuppressWarnings("all") #DIRECTORY=test #PACKAGE=.test
Miten ComTest.ini
tehdään? Itse kekkaloin sellaisen Notepadilla. Niinkö? Miten sen saa näkymään/kuuluuko sen näkyä Eclipsen Package Explorerissa?
VL: se on tekstitiedosto ja se on ihan sama millä (!= Word) sen kirjoittaa. Ja se näkyy normalisti hakemistossa, Eclipsessä sen ei tarvitse näkyä. Kun ComTest ajetaan, se kiipeää nykyhakemistosta ylöspäin kunnes tuo löytyy ja käyttää siinä olevia asetuksia. Eli sen voi sijoittaa niin ylös hakemistopuussa, ettei Eclipse siitä mitään edes tiedä.
Mitä Java-tiedostolla tässä tarkoitetaan? Sitä paikkaa, johon jdk-15…-hakemisto on sijoitettu?
VL: ei, tarkensin tekstiä.
—Mene sen aliohjelman JavaDoc kommentteihin, jota haluat testata
Tee loppuun yksi tyhjä rivi (siis sellainen jossa on * alussa)
Kirjoita comtest ja paina Ctrl-Space (katso CtrlSpaceEiToimi). Jos tulee paljon valintoja, niin valitse ComTest Example
Sinulle pitäisi tulla tyhjä "raami":
* @example * <pre name="test"> * * </pre>
Tähän raamiin kirjoitetaan testi. Esimerkiksi:
* @example * <pre name="test"> * poista("Catcat" ,"at") === "Cc"; * poista("Paatti" ,"at") === "Pi"; * poista("Puatit" ,"at") === "Puit"; * </pre>
Voi käyttää myös tarvittaessa esim:
#import fi.jyu.mit.ohj2.*;
Tallenna työsi ennen testien generoimista ja ajamista.
Muista lisätä JUnit 5 projektiisi (menusta:
Project/Properties/Java Build Path/Add Library/JUnit/JUnit 5/Finish
)Paina hiiren oikeata nappia ja valitse
ComTest/Generate, Run JUnit
Jos tulee käännösvirheitä, avaa syntynyt testi-ohjelma ja koeta keksiä virheen syy.
Jos tulee ajossa ilmoitus väärästä tuloksesta, niin mieti onko väärin testi vai toteutus. Korjaa.
Jos testi on sisennetty, JUnit ei välttämättä osaa poistaa sisennystä, vaan etsii esim aliohjelmaa " hypotenuusa()".
Minulle ei tule mitään ehdotuksia kun kirjoitan comtest ja painan ctrl + space
KR: Sun pitää olla metodikommentin sisässä:
/** Huom. tässä pitää olla kaksi tähteä.
* comtest
*/
— Jos aliohjelma palauttaa boolean arvon niin mitenkäs tämä sitten menne?
KR: Booleaneille vastaavasti esim. palautaTosi() === true;
Mistä johtuu että joskus args punainen viiva häviää kun kommentoidaan, joskus myös ei käytössä saa punaisen viivan ja sille ehdotetaan että lisää sanakirjaan.
VL: katso oikoluvun asetukset ja ota oikoluku pois päältä. Sitten args pitää kommentoida sillä on väärin esitellä parametrejä kommenteissa ja jättää kertomatta mitä ne ovat.
Mitä tuo ehdotettu #import fi.jyu.mit.ohj2.*; tekee esimerkkikoodissa? Pistin ten package-rivin jälkeen ja otin risuaidan pois ja koodi ei käyttänyt sitä mihinkään
VL: jos käytät tuolla kirjastossa olevia kutsua, pitää se importata
Oliko comtestissä millainen syntaksi jos haluaa testata esimerkiksi että jokin olio != null?
VL: lisäsin olioiden testaamisen kohdalle.
—Mikä on JavaDoc kommentti ja mistä sinne pääsee käsiksi Eclipsessä?
VL: Se on tuo mikä alkaa /**
ja sen saa kirjoittamalla noin.
Testasin aliohjelmassa Laskuja valmiina olleet testit, tätäkö tässä haettiin?
VL: Siis siihen riittää että työkalu toimii, jatkossa lisää.
—6. Kaikkien testien ajaminen kerralla
Harjoitustyön tekemisen yhteydessä kaikkien tiedostojen testaaminen yksitellen voi olla hidasta. Voit ajaa kaikki ComTest-testit kerralla seuraavasti:
- Valitse Package Explorerista ne tiedostot, jotka haluat testata. Jos esim. haluat testata jonkun paketin kaikki tiedostot, niin klikkaa paketin ylimmäistä tiedostoa, Shift pohjaan ja klikkaa alimmaista tiedostoa.
- Klikkaa hiiren oikeaa ja ComTest -> Generate tests.
- Valitse generoidut testitiedostot (yleensä riittää valita paketti, jossa ne sijaitsevat) ja klikkaa hiiren oikeaa -> Run As -> JUnit Test.
7. Hieman edistyneempi käyttö
Reaalilukuja testataan seuraavasti:
* @example * <pre name="test"> * #TOLERANCE=0.01 // Määrää vertailun tarkkuuden (huom ei välilyöntejä) * hypotenuusa(0,0) ~~~ 0.0; * hypotenuusa(0,1) ~~~ 1.0; * hypotenuusa(1,1) ~~~ 1.41; * hypotenuusa(1,2) ~~~ 2.24; * hypotenuusa(5,6) ~~~ 7.81; * </pre>
Totuusarvoja testataan seuraavasti:
* @example * <pre name="test"> * onkoIsolla("KISSA") === true; * onkoIsolla("Kissa") === false; * </pre>
Jos halutaan merkkijonoista "epämääräisyyksiä", käytetään Regular Expressionia:
* @example * <pre name="test"> * Henkilo2 hlo = new Henkilo2("Aku","Ankka",1934); * hlo.toString() =R= "Aku.*"; // Ensin pitää lukea Aku ja • // sitten saa olla mitä kirjainta (.) tahansa * </pre> // kuinka monta tahansa (*)
Muista että RegExpin kanssa voi joutua laittamaan kenoviivoja useampia, jotta ne saa menemään "mätserille" saakka. Esimerkiksi:
* @example * <pre name="test"> * "kissa\n" =R= "kissa\\s"; // \s olisi reg.expin "tyhjä merkki", * </pre> // mutta Java syö \:n pois joten tarvii olla \\
Tällöin on muistettava että RegExpin omat merkit tuovat oman haasteensa. Eli jos halutaan testata RegExpin avulla samaa kuin:
vali.toString() === "(3.0-4.0)";
pitää tämä kirjoittaa
vali.toString() =R= "\\(3\\.0-4\\.0\\)";
koska sulkumerkit ovat RegExpin merkkejä ja ne pitää siis "prefixata". Samoin pisteen kohdalla.
Jos halutaan testata niin, että desimaalimerkiksi tulee kumpia tahansa (piste tai pilkku) voidaan siinä käyttää tietysti lueteltua joukkoa:
vali.toString() =R= "\\(3[.,]0-4[.,]0\\)";
Lisäksi merkkijonoihin laitetuissa desimaaliluvuissa desimaalien määrä tuottaa aina oman haasteensa.
Lue "käyttöohjeita": http://www.mit.jyu.fi/vesal/kurssit/ohj2/comtest/
Onko näin, että tuo #TOLERANCE ei toimi jos sen kirjoittaa "#TOLERANCE = 0.01"? , vaan se pitää kirjoittaa aina kaikki yhteen "#TOLERANCE=0.01".
VL: joo, noi voi olla aika tiukkoja syntaksista (ehkä tarpeettomastikin)
—StringBuilderin
tapauksessa pitää tulos muuttaa ensin merkkijonoksi:* StringBuilder sb = new StringBuilder("Aku Ankka"); * vaihdaEtuJaSuku(sb); * sb.toString() === "Ankka Aku";
8. Testataan heittääkö poikkeuksen
Usein funktion pitää heittää poikkeus jos jotakin pahaa tapahtuu. Testataan seuraavassa tuleeko koodista indeksin ylityspoikeus:
* String jono = "Kissa";
* jono.charAt(12) === 'a'; #THROWS IndexOutOfBoundsException
Mikäli poikeusta ei heitetä, aiheuttaa tämä testin epäonnistumisen.
9. Taulukoiden testaaminen
Esimerkki taulukon sisällön testaamisesta
/**
* Käännetään taulukon alkiot päinvastaiseen järjestykseen
* @param t taulukko jonka alkiot käännetään
* @example
* <pre name="test">
* #import java.util.Arrays;
* int[] t = {2, 32, 76, 62, 31, 86};
* kaanna(t);
* Arrays.toString(t) === "[86, 31, 62, 76, 32, 2]";
* </pre>
*/
Onnistuuko kokonaisen String[] taulukon testaaminen jotenkin helposti? Ainakaan toString() avulla en saanut toimimaan.Eli esim. String [] t = {"a","b"}; Arrays.toString(t) === "[a, b]";
VL: Minusta toimii, tein tuohon alemmaksi esimerkin. Se vaan tarvitsee tuon importin
—Esimerkki taulukon luomisesta kutsussa:
/**
* Lasketaan yhteen taulukon kaikki luvut
* @param t taulukko jonka luvut lasketaan
* @return lukujen summa
* @example
* <pre name="test">
* int[] t = {1,2,3,4,5,6};
* summa(t) === 21;
* summa(new int[]{1,1,1,1,1,1}) === 6;
* </pre>
*/
Oliko tähän jotain ratkaisua ilmoilla? Mulla on sama ongelma, eli tuo metodi on tuntematon sitten kun se yrittää junitteja ajaa. Syntaksit ja näkyvyydet kokeiltu kymmeneen kertaan.
VL: joku timiin tallennettu koodiesimerkki tarvitaan.
Vastataas nyt pienen väännön jälkeen havaintoja:
Eli olen tehnyt demokerrat omiin projekteihinsa ja nyt unohtui tehdä projektille oma paketti. Java tiedostot meni johonkin default packageen ja siitä kohtaa loppui comtestin ymmärrys. Eli homma rupesi rokkaamaan kun teki oman paketin ja laittoi comtestattavat tiedostot sinne. Tässä tapauksessa comtest osasi luoda paketinNimi.test paketin johon junit-tiedosto meni. Näkyvyysongelmaltahan tuo alunperinkin vaikutti kun junitti ihmetteli että tämmönen metodi on undefined.
VL: Joo, se on jossakin sanottu että ComTest ei toimi jos ei ole pakettia koska ilman pakettia ei voi viitata vieraaseen aliohjelmaan.
—Huom! Kutsuun kirjoitettu 2-ulotteisen taulukkon luominen ei toistaiseksi toimi. 2-ulotteiset taulukot pitää toistaiseksi luoda apumuuttujaan.
10. Olioiden testaaminen
Olioita testataan niin, että luodaan testattava olio, tehdään sille jotakin mitä testin luonteen takia tarvitsee ja sitten tavalla tai toisella katsotaan että olion tila vastaa sen jälkeen toivottua tilaa. Usein joku toString
tai get
-metodi auttaa tilan tarkistamisessa.
Jos haluaa testata onko olioviite eri kuin null, pitää vähän soveltaa ja testata:
a == null === false; // tai
a != null === true;
11. Taulukkomuotoinen testaaminen
Opettele myös taulukkomuotoinen testaaminen, se vähentää monesti kirjoittamista.
Esimerkki kurssin Ali-paketista kun testataan staattista
tayta
funktiota:* @example * <pre name="testTayta"> * tayta($jono,$n) === $tulos; * * $jono | $n | $tulos * ------------------------------- * "kissa" | 3 | "kissakissakissa" * "" | 10 | "" * "kissa" | 0 | "" * "kissa" | 1 | "kissa" * "a" | 10 | "aaaaaaaaaa" * </pre>
Lyhyesti: dollarimerkki (
$
) merkitsee muuttujia, viivamerkit (-----..
) ovat koristeita, putkimerkit (|
) erottelevat muuttujia ja niiden arvoja.Huomaa että tolpan (
|
) ympärillä on oltava aina tyhjää, jotta osataan tulkita taulukkomuotoiseksi testiksi. Muuten tästä ei generoidu testejä ja aivan vääränlaisetkin rivit saatavat näyttää tuottavan vihreää. Muista AINA kokeilla, että saat myös punaista!Katso lisää esimerkkejä esim: kurssin Ali-paketista ja erityisesti Mjonot.java.
Ideana on siis että ensin kirjoitetaan testistä malliesimerkki ja sitten taulukossa kerrotaan mitä arvoja malliin laitetaan kullakin testiesiintymällä. Jokainen testiesiintymä saadaan sijoittamalla taulukosta vastaavan sarakkeen muuttujia mallitestiin.
Taulukkomuotoisessa testaamisessa on oltava huolellinen, ettei sama muuttuja tai olio-muuttuja tule esiteltyä useita kertoja. Tämä voi tehdä esittelemällä muuttujat ensin ja sitten laittamalla tyhjän rivin ja vasta sitten $
-merkillä varustettuja rivejä. Silloin tyhjällä rivillä erotettu osa tulee syntyvään JUnit-tiedostoon vain yhden kerran.
Edellä esimerkissä on vielä käytetty taulukkomuotoisen testaamisen ominaisuutta, että mikäli taulukossa on ---
jossakin sarakkeessa, niin sellaisia lauseita joissa vastaava sarake esiintyy, ei käännetä lopputulokseen.
Eli edellinen olisi sama kuin kirjoitettaisiin:
Siis ---
-arvon takia toiseen testijoukoon ei ole tullut
a = new Aika(---,---);
riviä. Vastaavasti viimeiseen testijoukkoon ei ole tullut lausetta
a.lisaa(---);
koska siinäkin olisi käytetty silloin sarkkeessa olevaa ---
-jonoa.
Toinen mahdollisuus välttää muuttujien nimien uudelleenesittely on sulkea koko testattava esimerkkiosa aaltosulkuihin. Tällöin joka testiesiintymää varten syntyy uusi muuttuja. Tällöin ei luonnollisesti voi em. esimerkissä käyttää ---
-arvoa $h
tai $m
sarakkeissa, koska muuuttuja a
jäisi kokonaan syntymättä siltä testiesiintymältä.
Yksi ComTestin "vika" on että kaikki testit menevät samaan JUnitin testifunktioon. Tästä on haittana se, että jos yksikin testi epäonnistuu, niin muita testejä ei enää ajeta.
Tarvittaessa voidaan erikseen pyytää, että muodostetaan kokonaan uusi testi jokaisesta testirivistä jonka edellä on ===
-jono. Tällöin vaikka ensimmäinen testi epäonnistuisi, muut kuitenkin ajetaan. Kokeile seuraavassa muutella eri testejä oikeaksi ja vääräksi.
12. Sisäluokkien testaaminen
Jos esimerkiksi on stattinen sisäluokka luokka Kierros
joka on kirjoitettu luokan Makihyppy
sisäluokaksi, niin silloin sen metodia voidaan testata esimerkiksi:
public class Makihyppy {
public static class Kierros {
...
* @example
* <pre name="test">
* var kierros = new Makihyppy.Kierros();
* kierros.setPituus(101);
* kierros.setTuomari(0, 0);
* kierros.setTuomari(1, 20);
* kierros.setTuomari(2, 20);
* kierros.getPisteet() ~~~ 61.0;
* </pre>
*/
public void setPituus(double pit) {
pituus = pit;
}
13. Testissä tarvitaan jotakin ulkopuolista luokkaa
Mikäli testitssä tarvitsee importata joku luokka, jota ei valmiina ole. voidan tämä sanoa esimerkiksi:
* @example
* <pre name="test">
* #import java.io.*;
* ... käytetään tarvittavia luokkia ...
* </pre>
14. OutputStreamin testaaminen
Funktio, jolle viedään parametrina OutputStream. Esimerkiksi:
public void tulosta(OutputStream os) { PrintStream out = new PrintStream(os); out.println("Tulostetaan jotain"); }
Tätä kutsutaan
tulosta(System.out);
Testaus voidaan tehdä seuraavasti:
* <pre name="test"> * #import java.io.ByteArrayOutputStream; * ByteArrayOutputStream outContent = new ByteArrayOutputStream(); * Henkilo hlo = new Henkilo("Antti","Virtanen", 30); * hlo.tulosta(outContent); * outContent.toString() === "Antti Virtanen, 30v\r\n"; // Varoitus: Kaikissa järjestelemissä ei ole sama rivin loppu!
Huom!
outContent.toString
antaa myös rivinerotinmerkin, kun siihen on ne tulostettu. Vertaillessa ne pistää poistaa tai lisätään vertailutekstiin kuten yllä.\r\n
sijaan olisi hyvä käyttääSystem.getProperty("line.separator")
-kutsun antamaa merkkiä, joka antaa kulloisenkin järjestelmän rivierottimen.
Seuraava esimerkki havainnollistaa Comtestin ja JUnitin suhdetta testaten eri rivinvaihtomerkkien käsittelyä.
Comtest:
/**
* @example
* <pre name="test">
* System.lineSeparator() === "\r\n";
* System.lineSeparator().equals("\r\n") === true;
* System.lineSeparator().equals("\n") === false;
*
* String mjono = "\t \r\n abc \n";
* "abc" === mjono.trim();
* "abc" === mjono.strip();
* </pre>
*/
TAI (Comtest):
/**
* @example
* <pre name="test">
* #import static org.junit.Assert.assertEquals;
* #import static org.junit.Assert.assertTrue;
* #import static org.junit.Assert.assertFalse;
*
* assertEquals(System.lineSeparator(), "\r\n");
* assertTrue(System.lineSeparator().equals("\r\n"));
* assertFalse(System.lineSeparator().equals("\n"));
*
* String mjono = "\t \r\n abc \n";
* assertEquals("abc", mjono.trim());
* assertEquals("abc", mjono.strip());
* </pre>
*/
JUnit:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class Junit {
@Test
public void testAll() {
assertEquals(System.lineSeparator(), "\r\n");
assertTrue(System.lineSeparator().equals("\r\n"));
assertFalse(System.lineSeparator().equals("\n"));
String mjono = "\t \r\n abc \n";
assertEquals("abc", mjono.trim());
assertEquals("abc", mjono.strip());
}
}
15. Tulostuksen testaaminen
Tämä tarvitsee että Ali.jar on projektissa.
Katso Suuntaaja.
Esimerkki:
* @example
* <pre name="test">
* Suuntaaja.StringInput si = new Suuntaaja.StringInput("");
* Suuntaaja.StringOutput so = new Suuntaaja.StringOutput();
*
* si.input("ä 8\n8 5\n4 5\n\n");
* main(null);
* String tulos =
* "Käytössäsi on 5.0 sekä 8.0 litran astiat ja Ämpari (0.0 l)\n"+
* "5.0 litran astiassa on 0.0 litraa nestettä\n" +
* "8.0 litran astiassa on 0.0 litraa nestettä\n" +
* "Mistä kaadetaan ja mihin >" +
* "5.0 litran astiassa on 0.0 litraa nestettä\n" +
* "8.0 litran astiassa on 8.0 litraa nestettä\n" +
* "Mistä kaadetaan ja mihin >" +
* "5.0 litran astiassa on 5.0 litraa nestettä\n" +
* "8.0 litran astiassa on 3.0 litraa nestettä\n" +
* "Mistä kaadetaan ja mihin >" +
* "Nimeä ei tunneta: 4 tai 5\n" +
* "Tunnetaan nimet: \n" +
* "ä 5 8 \n" +
* "5.0 litran astiassa on 5.0 litraa nestettä\n" +
* "8.0 litran astiassa on 3.0 litraa nestettä\n" +
* "Mistä kaadetaan ja mihin >";
*
* so.ero(tulos) === null;
* si.palauta(); so.palauta();
* </pre>
Tämä saattaa vaatia, että heti luokan alkuun listäään
// #import fi.jyu.mit.ohj2.Suuntaaja;
jotta testitiedostoon tulee oikea import.
16. Tiedostojen testaaminen
Tiedostojen testaamiseksi on joukko apualiohjelmia luokassa VertaaTiedosto.
Esimerkiksi testataan ohjelmaa (eli ajetaan sen main
-aliohejlma), jolle komentorivin argumenttina annetaan mitä tiedostoa luetaan (esimerkissä hiljaa.txt
ja mihin tiedostoon tulos kirjoitetaan (esimerkissä tulos.txt
).
/**
* Yli 30 alkavien rivien kopiointi.
* @param args pääohjelman argumentit mistä mihin
*
* @example
* <pre name="test">
* #THROWS IOException
* #import java.io.IOException;
* #import fi.jyu.mit.ohj2.VertaaTiedosto;
* VertaaTiedosto.kirjoitaTiedosto("hiljaa.txt",
* "33 hiljaa 1 hiipii\n"+
* "hyvä 33 tulee\n"+
* "36 1 3 5 55\n"+
* "nyt 33 riittää\n");
* String tulos =
* "33 hiljaa 1 hiipii\n"+
* "36 1 3 5 55\n";
* VertaaTiedosto.tuhoaTiedosto("tulos.txt");
* main(new String[]{"hiljaa.txt","tulos.txt"});
* VertaaTiedosto.vertaaFileString("tulos.txt",tulos) === null;
* VertaaTiedosto.tuhoaTiedosto("tulos.txt");
* VertaaTiedosto.tuhoaTiedosto("hiljaa.txt");
* </pre>
*/
Esimerkissä on luettavasta tiedostosta tulostetaan toiseen tiedostoon rivit joiden alussa on luku joka on suurempi kuin 30. Testin päätteeksi on kohteliasta tuhota testin aikana syntyneet "resurssit". Toki tiedostojen niminä voisi käyttää sellaisiakin, joita ei levyllä voi olettaa etukäteen olevan.
Testin on itse luotava tarvittavat resurssit, se ei voi luottaa siihen, että jokin resurssi olisi olemassa. Esimerkissä VertaaTiedosto.kirjoitaTiedosto
tekee tämän. Odotettu tulos on tehty merkkijonoon tulos
ja kun pääohjelma on ajettu, verrataan onko syntyneen tulos.txt
-tiedoston sisältä sama kuin merkkijonon tulos
sisältö (ottaen huomioon mahdolliset erilaiset rivinvaihtomerkit eri järjestelmien välillä). Tämän vertailun tekee esimerkissä VertaaTiedosto.vertaaFileString
. Funktio palauttaa null
, mikäli sisälöt vastaavat toisiina. Mikäli eivät vastaa, palautetaan merkkijono, jossa on eroavuudet paikat.
Toki main
-funktio kutsun tilalla voi olla mikä tahansa muukin aliohjelmakutsu tai metodikutsu.
Pakko kysyä: tuossa on VertaaTiedosto.tuhoaTiedosto("tulos.txt"). Missä tuo "tulos.txt" luodaan? Ylempänä on String tulos mutta se ei kuitenkaan käsittääkseni tule .txt muotoon - ainakin ylempänä esim. "hiljaa.txt" luodaan erikseen. Sitä myöhemmin verrataan tulos-Stringiin.
VL: Tarkensin tekstillä.
—17. Vanhat ComTestin versiot
Uudet ComTest-versiot, 1.06:sta asti eivät tarvitse alla olevia
Lisää johonkin kohti vielä kommentteihin tällaisen testin tapauksessa
// #STATICIMPORT
jotta kääntäjä osaa käyttää poista-metodia oikeasta luokasta ilman viitettä luokkaan. Huom! Kommenttimerkin ja #:n välissä saa olla korkeintaan yksi välilyönti ja sen on oltava välilyönti. JavaDoc kommentteihin kirjoitettuna *-merkin perään taas ei saa olla noita //-merkkejä ettei tule sisäkkäisiä kommentteja. Toki voit kirjoittaa testeissä myös:
* Poista.poista("Catcat" ,"at") === "Cc";
mutta
#STATICIMPORT
-rivin ansiosta luokkaa ei ole pakko mainita testeissä. Toinen usein tarvittava import voi olla#CLASSIMPORT
, jolloin luokka lisätään import-lauseeseen.
18. Ongelmia
- joskus kannattaa tuhota vanhat testitiedostot, koska jos ne on saatu rikki, niin ComTest ei uudelleen generoinnissa muuta muita kuin itse kirjoittamiaan osia.
19. Yksinkertaisten Comtest-testien suorittaminen yhdellä komennolla bash-ympäristössä
Yksittäisten luokkien Comtest-testien ajamista voi nopeuttaa vähäisellä vaivalla pienellä shell-skriptillä. Menetelmä sopii pienten ohjelmien (joidenkin demovastauksien) ohjelmointiin Linux-ympäristössä tekstieditorilla, mutta sopivilla muutoksilla skriptiä voi toki laajentaa koskettamaan laajempiakin ohjelmia.
19.1 Valmistelut
Asenna ensin Comtest ohjeiden mukaisesti ja lue myös sivu käytöstä ilman pluginia
Varmista että tarvittavat tiedostot ovat ympäristömuuttujissa:
#Polut luonnollisesti vaihtelevat koneittain echo 'CLASSPATH="$CLASSPATH:/home/username/ohj2/Ali.jar:/usr/share/junit-4/lib/junit.jar:/usr/share/hamcrest-core/lib/hamcrest-core.jar"' >> ~/.bash_profile
Luo kansio, joka on PATH-muuttujassa ja jonne voi tallentaa suoritettavia ohjelmia.
mkdir ~/bin echo 'PATH="$PATH:~/bin"' >> ~/.bash_profile
Lataa päivitetyt ympäristömuuttujat joko kirjautumalla uudelleen tai kirjoittamalla
source ~/.bash_profile
19.2 Bash-skripti
Seuraava skripti ei suorita minkäänlaista virheentarkistusta, eli toimivuus on odotettavissa ainoastaan mikäli ohjelma ja testit kääntyvät oikein. Esimerkkitapauksessa skriptin nimenä toimii 'jtest'.
Avaa tekstieditorilla kohdetiedosto, esim:
nano ~/bin/jtest
Kirjoita jotain seuraavankaltaista tiedostoon:
#!/bin/bash PREF="jtest:" #etuliite skriptin tulosteille TARGET=${1%.java} #riisutaan pääte echo "${PREF} Käännetään ${TARGET}.java ..." javac ${TARGET}.java echo "${PREF} JUnit-testien generointi Comtestilla ..." java -jar /home/username/ohj2/comtest.jar ${TARGET}.java echo "${PREF} Käännetään JUnit-testi ${TARGET}Test.java ..." javac ${TARGET}Test.java echo "${PREF} Ajetaan yksikkötesti ..." java org.junit.runner.JUnitCore ${TARGET}Test
Lopuksi vielä tee tekstitiedostosta suoritettava
chmod +x ~/bin/jtest
19.3 Käyttö
Jos valmistelut sujuivat onnistuneesti, käytön pitäisi toimia seuraavasti:
jtest Yksikko.java
Joka tuottaa esimerkiksi seuraavannäköistä ulostuloa:
jtest: Käännetään Yksikko.java ...
jtest: JUnit-testien generointi Comtestilla ...
Yksikko.java => /home/username/ohj2/demo5/YksikkoTest.java ok
jtest: Käännetään JUnit-testi YksikkoTest.java ...
jtest: Ajetaan yksikkötesti ...
JUnit version 4.6
.
Time: 0.003
OK (1 test)
19.4 Mallikerhon testaaminen komentoriviltä
Lataa comtest.jar hakemistoon
\devel\jar
nimelläcomtest.jar
Lataa junit.jar hakemistoon
\devel\jar
nimelläjunit.jar
Hae kerho versionhallinnasta
svn export https://svn.cc.jyu.fi/srv/svn/ohj2ht/k2019/vesal/trunk/ cd trunk
Käännä testattavat luokat JUnit-testejä varten
javac -d bin -cp \devel\jar\Ali.jar;\devel\jar\fxgui.jar --module-path \devel\javafx\lib --add-modules javafx.fxml,javafx.controls,javafx.web src\kanta\*.java src\kerho\*.java src\fxKerho\*.java
Luo JUnit-testit makrokielen mukaisista kommenteista
java -jar \devel\jar\comtest.jar src\kanta\*.java src\kerho\*java src\fxKerho\*.java
Käännä testattavien luokkien JUnit-testit
javac -d bin -cp \devel\jar\Ali.jar;\devel\jar\fxgui.jar;\devel\jar\junit.jar;bin --module-path \devel\javafx\lib --add-modules javafx.fxml,javafx.controls,javafx.web src\kanta\test\*.java src\kerho\test\*.java src\fxKerho\test\*.java src\test\*.java
Aja testit
cd bin java -cp .;\devel\jar\junit.jar;\devel\jar\Ali.jar org.junit.runner.JUnitCore test.AllTests
pitäisi tulostaa
JUnit version 4.10 ................................................................................ .................................................. Time: 0,484 OK (130 tests)
These are the current permissions for this document; please modify if needed. You can always modify these permissions from the manage page.