Skip to content

Viikon 4 laskareissa tehtävä tehtävä

Lassi Vapaakallio edited this page Aug 3, 2015 · 3 revisions

1 Lyyrakortti ja kassapääte: testit kortille

Ohjelmoinnin perusteiden viikolla 5 tehtävissä 86 toteutettiin “tyhmä” Lyyrakortti ja Kassapääte.

Rahan käsittely double-tyyppisenä on hiukan ongelmallista. Seuraavassa rahaa käsitellän kokonaislukuna ja kaikki rahamäärät talletetaan eurojen sijasta sentteinä.

Ohjelmointiuransa aloittelevan tuttavasi vastaus seuraavassa:

public class LyyraKortti {
 
    private int saldo;
 
    public LyyraKortti(int saldo) {
        this.saldo = saldo;
    }
 
    public int saldo() {
        return saldo;
    }
 
    public void lataaRahaa(int lisays) {
        this.saldo += lisays;
    }
 
    public boolean otaRahaa(int maara) {
        if (this.saldo < maara)
            return false;
 
        this.saldo = this.saldo - maara;
        return true;
    }

    @Override
    public String toString() {
        int euroa = saldo/100;
        int senttia = saldo%100;
        return "saldo: "+euroa+"."+senttia;
    }    
}

Kassapäätteelle on lisätty metodit jotka mahdollistavat myytyjen lounaiden määrän ja kassassa olevan rahamäärän kysymisen, toString()-metodi on poistettu:

public class Kassapaate {
 
    private int kassassaRahaa;
    private int edulliset;
    private int maukkaat;
 
    public Kassapaate() {
        this.kassassaRahaa = 100000;
    }
 
    public int syoEdullisesti(int maksu) {
        if (maksu >= 240) {
            this.kassassaRahaa = kassassaRahaa + 240;
            ++this.edulliset;
            return maksu - 240;
        } else {
            return maksu;
        }
    }
 
    public int syoMaukkaasti(int maksu) {
        if (maksu >= 400) {
            this.kassassaRahaa = kassassaRahaa + 400;
            this.maukkaat++;
            return maksu - 400;
        } else {
            return maksu;
        }
    }
 
    public boolean syoEdullisesti(LyyraKortti kortti) {
        if (kortti.saldo() >= 240) {
            kortti.otaRahaa(240);
            this.edulliset++;
            return true;
        } else {
            return false;
        }
    }
 
    public boolean syoMaukkaasti(LyyraKortti kortti) {
        if (kortti.saldo() >= 400) {
            kortti.otaRahaa(400);
            this.maukkaat++;
            return true;
        } else {
            return false;
        }
    }
 
    public void lataaRahaaKortille(LyyraKortti kortti, int summa) {
        if (summa >= 0) {
            kortti.lataaRahaa(summa);
            this.kassassaRahaa += summa;
        } else {
            return;
        }
    }
 
    public int kassassaRahaa() {
        return kassassaRahaa;
    }
 
    public int maukkaitaLounaitaMyyty() {
        return maukkaat;
    }
 
    public int edullisiaLounaitaMyyty() {
        return edulliset;
    }
}

Hae täältä zip-pakattu projekti, jossa Lyyrakortti ja Kassapääte ovat valmiina. Pura paketti ja avaa projekti NetBeansilla. Projekti ei ole samanlainen kuin normaalisti käyttämämme NetBeans-projektit vaan ns. Maven-projekti. Voit toimia kuitenkin täysin samaan tapaan kuin olet aina käyttänyt NetBeansia. Ensimmäisellä suorituskerroilla ohjelman suorituskerroilla ohjelman suorittamiseen menee hieman normaalia kauemmin.

Testien suorittaminen onnistuu valitsemalla Run ja Test project tai painamalla Alt+F6. JUnit-ohjeessa neuvottiin myös miten saat lisättyä oman testiensuoritusnapin.

tai vaihtoehtoisesti ollessasi projektihakemistossa antamalla komentoriviltä komento mvn test

Huom: NB:n oikeaan reunaan tulee ilmoitus “unpacking central repository”. Klikkaa ilmoitusta ja lopeta toimenpide.

Tehdään JUnit-testit ensin Lyyrakortille.

Tee valmiiseen testiluokkaan LyyraKorttiTest testit jotka testaavat ainakin seuraavia asioita:

  • luodulla kortilla on konstruktorin parametrina määritelty saldo
  • rahan lataaminen kasvattaa saldoa oikein
  • rahan ottaminen toimii:
    • saldo vähenee oikein jos rahaa on tarpeeksi
    • saldo ei muutu jos rahaa ei ole tarpeeksi
    • metodi palauttaa true jos rahat riittivät ja muuten false

HUOM: jos törmäät outoihin ongelmiin koodin tai testien toimivuuden suhteen, paina vasara+harja-symbolia (clean and build)

2 Testauskattavuus

Olemme tyytyväisiä, uskomme että testitapauksia on nyt tarpeeksi. Onko tosiaan näin? Onneksi on olemassa työkaluja, joilla voidaan tarkastaa testien lause- ja haarautumakattavuun. Lausekattavuus mittaa mitä koodirivejä testien suorittaminen on tutkinut. Täydellinen lausekattavuuskaan ei tietenkään takaa että ohjelma toimii oikein, mutta on parempi kuin ei mitään. Haarautumakattavuus taas mittaa mitä eri suoritushaarjoa koodista on käyty läpi. Suoritushaaroilla tarkoitetaan esim. if-komentijen valintatilanteita.

Projektitiedostoon on valmiiksi konfiguroitu käytettäväksi Cobertura joka mittaa sekä lause- että haarautumakattavuuden.

Kattavuuden mittaus suoritetaan klikkaamalla NB:stä projektin kohdalla oikeaa nappia ja valitsemalla custom/cobertura

tai vaihtoehtoisesti ollessasi projektihakemistossa antamalla komentoriviltä komento mvn cobertura:cobertura

Tulokset tulevat projektihakemistosi alihakemistoon target/site/cobertura/index.html. Avaa tulokset web-selaimella. Firefoxilla tämä tapahtuu komennolla open file. Voit myös avata selaimen terminaalissa menemällä ensin projektihakemistoon ja antamalla komento chromium-browser target/site/cobertura/index.html

Jos lyyrakortin koodissa on vielä rivejä tai haarautumia (merkitty punaisella) joille ei ole testiä, kirjoita sopivat testit.

3 Testien testaaminen

Pelkkä koodirivien kattaminen testeillä ei riitä.

On mahdollista kirjoittaa testejä jotka “koskevat” kaikkia koodirivejä mutta eivät testaa oikein mitän järkevää. Pelkän suuren kattavuuden tavoittelun lisäksi on siis tärkeä muistaa testata:

  • normaali toimina
  • poikkeuksellinen toiminta
  • virhetilanteet

Erityinen huomio kannattaa kiinnittää ns. raja-arvoihin:

  • toimiiko kortilla ostaminen jos rahaa on jäljellä täsmälleen lounaan hinnan verran?

Yksi tapa testien hyvyyden testaamiseen on mutaatiotestaus. Testaamme seuraavaksi testiemme hyvyyttä PIT-työkalun avulla.

Seuraavassa PIT:in sivulta otettu selitys joka kertoo mistä mutaatiotestauksessa on kyse:

Mutation testing is conceptually quite simple. Faults (or mutations) are automatically seeded into your code, then your tests are run. If your tests fail then the mutation is killed, if your tests pass then the mutation lived.

The quality of your tests can be gauged from the percentage of mutations killed.

To put it another way – PIT runs your unit tests against automatically modified versions of your application code. When the application code changes, it should produce different results and cause the unit tests to fail. If a unit test does not fail in this situation, it may indicate an issue with the test suite.

Mutaatiotestaa ohjelmasi suorittamalla ensin testit ja sen jälkeen klikkaamalla NB:stä projektin kohdalla oikeaa nappia ja valitsemalla custom/pit

tai vaihtoehtoisesti ollessasi projektihakemistossa antamalla komentoriviltä komennot mvn test ja mvn org.pitest:pitest-maven:mutationCoverage

Edellinen onnistuu myös yhdellä komennolla mvn test org.pitest:pitest-maven:mutationCoverage

Tulokset tulevat projektihakemistosi alihakemistoon target/pit-reports/aikaleima/index.html. Aikaleima on numerosarja joka kertoo suoritushetken, tyyliin 201311181742 (testi ajettu 18.11.2013 klo 1742). Avaa tulokset web-selaimella. Firefoxilla tämä tapahtuu komennolla open file. Voit myös avata selaimen terminaalissa menemällä ensin projektihakemistoon ja antamalla komento chromium-browser target/pit-reports/aikaleima/index.html. Kun ajat mutaatiotestit uudelleen, muista avat oikea raportti!

Voit poistaa vanhat raportit painamalla vasara+harja-symbolia (clean and build) tai komentoriviltä komennolla mvn clean

Raportista näet mitkä mutantit jäävät henkiin.

Yritä parantaa testejäsi siten, että lausekattavuus säilyy ja kaikki mutantit kuolevat.

4 Kassapäätteen testit

Laajennetaan Unicafen testaus kattamaan myös kassapääte.

Tee testiluokka KassapaateTest  ja tee testit jotka testaavat ainakin seuraavia asioita:

  • luodun kassapäätteen rahamäärä ja myytyjen lounaiden määrä on oikea (rahaa 1000, lounaita myyty 0)
  • käteisosto toimii sekä edullisten että maukkaiden lounaiden osalta
    • jos maksu riittävä: kassassa oleva rahamäärä kasvaa lounaan hinnalla ja vaihtorahan suuruus on oikea
    • jos maksu on riittävä: myytyjen lounaiden määrä kasvaa
    • jos maksu ei ole riittävä: kassassa oleva rahamäärä ei muutu, kaikki rahat palautetaan vaihtorahana ja myytyjen lounaiden määrässä ei muutosta
  • seuraavissa testeissä tarvitaan myös Lyyrakorttia jonka oletetaan toimivan oikein
  • korttiosto toimii sekä edullisten että maukkaiden lounaiden osalta
    • jos kortilla on tarpeeksi rahaa, velotetan summa kortilta ja palautetaan true
    • jos kortilla on tarpeeksi rahaa, myytyjen lounaiden määrä kasvaa
    • jos kortilla ei ole tarpeeksi rahaa, kortin rahamäärä ei muutu, myytyjen lounaiden määrä muuttumaton ja palautetaan false
    • kassassa oleva rahamäärä ei muutu kortilla ostettaessa
  • kortille rahaa ladattaessa kortin saldo muuttuu ja kassassa oleva rahamäärä kasvaa ladatulla summalla

Huomaat että kassapääte sisältää melkoisen määrän “copypastea”.  Nyt kun kassapäätteellä on automaattiset testit on sen rakennetta helppo muokata eli refaktoroida siistimmäksi koko ajan kuitenkin varmistaen että testit menevät läpi. Tulemme tekemään refaktoroinnin seuraavalla viikolla.

5

Varmista että kassapäätteen teksteillä on 100% lause- ja haarautumakattavuus ja että mutantteja ei jää henkiin. Negatiivisen saldon lataamiseen liittyvää mutanttia on mahdoton tappaa. Tämä johtuu siitä, että nollan lataaminen ei aiheuta saldoon muutosta, joten vaikka ehdon muuttaisi negaatioksi, nollan lataaminen toimii edelleen.