*Allaolevien tehtävien deadline on *

Apua tehtävien tekoon kurssin Discord-kanavalla, kampuksella ja Zoomissa:

  • Maanantaisin (poislukien 6.12.) klo 14-16 kampuksella BK107-luokassa
  • Torstaisin (4.11. alkaen) klo 14-16 Zoomissa

Ohjauskalenteri:

Muista myös tämän viikon monivalintatehtävät, joiden deadline on .

Tehtävissä 1-4 tutustutaan yksikkötestausta helpottavaan Mockito-kirjastoon. Tehtävissä 5 ja 6 refaktoroidaan sisäiseltä laadultaan heikossa kunnossa olevaa koodia.

Typoja tai epäselvyyksiä tehtävissä?

Tee korjausehdotus editoimalla tätä tiedostoa GitHubissa.

Tehtävien palauttaminen

Tehtävät palautetaan GitHubiin, sekä merkitsemällä tehdyt tehtävät palautussovellukseen https://study.cs.helsinki.fi/stats/courses/ohtu-avoin-2022

Katso tarkempi ohje palautusrepositorioita koskien täältä.

1. Yksikkötestaus ja riippuvuudet: Mockito, osa 1

Useimmilla luokilla on riippuvuuksia toisiin luokkiin. Esim. viikon 2 laskarien verkkokaupan luokka Kauppa riippui Pankista, Varastosta ja Viitegeneraattorista. Riippuvuuksien injektion ja rajapintojen avulla saimme mukavasti purettua riippuvuudet luokkien väliltä.

Vaikka luokilla ei olisikaan riippuvuuksia toisiin luokkiin, on tilanne edelleen se, että luokan oliot käyttävät joidenkin toisten luokkien olioiden palveluita. Tämä tekee yksikkötestauksesta välillä hankalaa. Miten esim. luokkaa Kauppa tulisi testata? Tuleeko kaupan testeissä olla mukana toimivat versiot kaikista sen riippuvuuksista?

Olemme jo muutamaan otteeseen (esim. Nhl-Statsreader-tehtävässä viikolla 1 ratkaisseet asian ohjelmoimalla riippuvuuden korvaavan “tynkäkomponentin”. Javalle kuten kaikille muillekin kielille on tarjolla myös valmiita kirjastoja tynkäkomponenttien, toiselta nimeltään mock-olioiden luomiseen.

Kuten pian huomaamme, mock-oliot eivät ole pelkkiä “tynkäolioita”, mockien avulla voi myös varmistaa, että testattava luokka kutsuu olioiden metodeja asiaankuuluvalla tavalla.

Tutustumme nyt Mockito-nimiseen mock-kirjastoon.

Hae kurssirepositorion hakemistossa koodi/viikko4/MockitoDemo oleva projekti. Kyseessä on yksinkertaistettu versio Verkkokauppaesimerkistä.

Kaupan toimintaperiaate on yksinkertainen:

Pankki myNetBank = new Pankki();
Viitegeneraattori viitteet = new Viitegeneraattori();
Kauppa kauppa = new Kauppa(myNetBank, viitteet);

kauppa.aloitaOstokset();
kauppa.lisaaOstos(5);
kauppa.lisaaOstos(7);
kauppa.maksa("1111");

Ostokset aloitetaan tekemällä metodikutsu aloitaOstokset. Tämän jälkeen “ostoskoriin” lisätään tuotteita, joiden hinta kerrotaan metodin lisaaOstos parametrina. Ostokset lopetetaan kutsumalla metodia maksa joka saa parametriksi tilinumeron miltä summa veloitetaan.

Kauppa tekee veloituksen käyttäen tuntemaansa luokan Pankki olioa. Viitenumerona käytetään luokan Viitegeneraattori generoimaa numeroa.

Projektiin on kirjoitettu kuusi Mockitoa hyödyntävää testiä. Testit testaavat, että kauppa tekee ostoksiin liittyvän veloituksen oikein, eli että se kutsuu pankin metodia maksa oikeilla parametreilla, ja että jokaiselle laskutukselle on kysytty viitenumero viitegeneraattorilta. Testit siis eivät kohdistu olion pankki tilaan vaan sen muiden olioiden kanssa käymän interaktion oikeellisuuteen. Testeissä kaupan riippuvuudet (Pankki ja Viitegeneraattori) on määritelty mock-olioina.

Seuraavassa testi, joka testaa, että kauppa kutsuu pankin metodia oikealla tilinumerolla ja summalla:

@Test
public void kutsutaanPankkiaOikeallaTilinumerollaJaSummalla() {
    Pankki mockPankki = mock(Pankki.class);
    Viitegeneraattori mockViite = mock(Viitegeneraattori.class);

    kauppa = new Kauppa(mockPankki, mockViite);

    kauppa.aloitaOstokset();
    kauppa.lisaaOstos(5);
    kauppa.lisaaOstos(5);
    kauppa.maksa("1111");

    verify(mockPankki).maksa(eq("1111"), eq(10), anyInt());
}

Testi siis aloittaa luomalla kaupan riippuvuuksista mock-oliot:

Pankki mockPankki = mock(Pankki.class);
Viitegeneraattori mockViite = mock(Viitegeneraattori.class);

kauppa = new Kauppa(mockPankki, mockViite);

kyseessä eivät ole normaalit oliot vaan normaaleja olioita “matkivat” valeoliot, jotka myös pystyvät tarkastamaan, että niiden metodeja on kutsuttu oikeilla parametreilla.

Testi tarkastaa, että kaupalle tehdyt metodikutsut aiheuttavat sen, että pankin mock-olion metodia maksa on kutsuttu oikeilla parametreilla. Kolmanteen parametriin eli viitenumeroon ei kiinnitetä huomiota:

verify(mockPankki).maksa(eq("1111"), eq(10), anyInt());

Mock-olioille tehtyjen metodikutsujen paluuarvot on myös mahdollista määritellä. Seuraavassa määritellään, että viitegeneraattori palauttaa arvon 55 kun sen metodia seuraava kutsutaan:

@Test
public void kaytetaanMaksussaPalautettuaViiteta() {
    Pankki mockPankki = mock(Pankki.class);
    Viitegeneraattori mockViite = mock(Viitegeneraattori.class);

    // määritellään viitegeneraattorin metodikutsun vastaus
    when(mockViite.seuraava()).thenReturn(55);

    kauppa = new Kauppa(mockPankki, mockViite);

    kauppa.aloitaOstokset();
    kauppa.lisaaOstos(5);
    kauppa.lisaaOstos(5);
    kauppa.maksa("1111");

    verify(mockPankki).maksa(eq("1111"), eq(10), eq(55));
}

Testin lopussa varmistetaan, että pankin mockolioa on kutsuttu oikeilla parametrinarvoilla, eli kolmantena parametrina tulee olla viitegeneraattorin palauttama arvo.

Tutustu projektiin ja sen kaikkiin testeihin.

Testit suoritetaan normaaliin tapaan komennolla gradle test

Riko joku testi, esim. edellä listattu muuttamalla sen ekspektaatiota:

verify(mockPankki).maksa(eq("1111"), eq(10), eq(54));

ja varmista että testi ei mene läpi. Katso miltä virheilmoitus näyttää.

Mockiton dokumentaatio löytyy osoitteesta http://site.mockito.org

2. Yksikkötestaus ja riippuvuudet: Mockito, osa 2

Hae kurssirepositorion hakemistossa koodi/viikko4/MaksukorttiMockito oleva projekti.

Tässä tehtävässä on tarkoitus testata ja täydentää luokkaa Kassapaate. Maksukortin koodiin ei tehtävässä saa koskea ollenkaan! Testeissä ei myöskään ole tarkoitus luoda konkreettisia instansseja maksukortista, testien tarvitsemat kortit tulee luoda mockitolla.

Projektissa on valmiina kaksi testiä:

package ohtu.maksukortti;

public class KassapaateTest {
    
    Kassapaate kassa;
    Maksukortti kortti;
    
    @Before
    public void setUp() {
        kassa = new Kassapaate();
        kortti = mock(Maksukortti.class);
    }
    
    @Test
    public void kortiltaVelotetaanHintaJosRahaaOn() {
        when(kortti.getSaldo()).thenReturn(10);
        kassa.ostaLounas(kortti);
        
        verify(kortti, times(1)).getSaldo();
        verify(kortti).osta(eq(Kassapaate.HINTA));
    }

    @Test
    public void kortiltaEiVelotetaJosRahaEiRiita() {
        when(kortti.getSaldo()).thenReturn(4);
        kassa.ostaLounas(kortti);
        
        verify(kortti, times(1)).getSaldo();
        verify(kortti, times(0)).osta(anyInt());
    }
}

Ensimmäisessä testissä varmistetaan, että jos kortilla on riittävästi rahaa, kassapäätteen metodin ostaLounas kutsuminen kysyy kortin saldon ja velottaa summan kortilta.

Testi ottaa siis kantaa ainoastaan siihen miten kassapääte kutsuu maksukortin metodeja. Maksukortin saldoa ei erikseen tarkasteta, sillä oletuksena on, että maksukortin omat testit varmistavat kortin toiminnan.

Toinen testi varmistaa, että jos kortilla ei ole riittävästi rahaa, kassapäätteen metodin ostaLounas kutsuminen kysyy kortin saldon mutta ei veloita kortilta rahaa.

Testit eivät mene läpi. Korjaa kassapäätteen metodi ostaLounas.

Tee tämän jälkeen samaa periaatetta noudattaen seuraavat testit:

  • kassapäätteen metodin lataa kutsu lisää maksukortille ladattavan rahamäärän käyttäen kortin metodia lataa jos ladattava summa on positiivinen
  • kassapäätteen metodin lataa kutsu ei tee maksukortille mitään jos ladattava summa on negatiivinen

Korjaa kassapäätettä siten, että testit menevät läpi.

3. Yksikkötestaus ja riippuvuudet: Mockito, osa 3

Testataan viikolta 2 tutun Verkkokaupan luokkaa Kauppa.

Kaupalle injektoidaan konstruktorissa Pankki, Viitelaskuri ja Varasto. Tehdään näistä testeissä Mockitolla mockatut versiot.

Seuraavassa esimerkkinä testi, joka testaa, että ostostapahtuman jälkeen pankin metodia tilisiirto on kutsuttu:

package ohtu.verkkokauppa;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

public class KauppaTest {

    @Test
    public void ostoksenPaaytyttyaPankinMetodiaTilisiirtoKutsutaan() {
        // luodaan ensin mock-oliot
        Pankki pankki = mock(Pankki.class);

        Viitegeneraattori viite = mock(Viitegeneraattori.class);
        // määritellään että viitegeneraattori palauttaa viitten 42
        when(viite.uusi()).thenReturn(42);

        Varasto varasto = mock(Varasto.class);
        // määritellään että tuote numero 1 on maito jonka hinta on 5 ja saldo 10
        when(varasto.saldo(1)).thenReturn(10); 
        when(varasto.haeTuote(1)).thenReturn(new Tuote(1, "maito", 5));

        // sitten testattava kauppa 
        Kauppa k = new Kauppa(varasto, pankki, viite);              

        // tehdään ostokset
        k.aloitaAsiointi();
        k.lisaaKoriin(1);     // ostetaan tuotetta numero 1 eli maitoa
        k.tilimaksu("pekka", "12345");

        // sitten suoritetaan varmistus, että pankin metodia tilisiirto on kutsuttu
        verify(pankki).tilisiirto(anyString(), anyInt(), anyString(), anyString(),anyInt());   
        // toistaiseksi ei välitetty kutsussa käytetyistä parametreista
    }
}

Aloita siten, että saat esimerkkitestin toimimaan.

Muista lisätä build.gradle tiedostoon riippuvuudeksi Mockito. Katso mallia edellisten tehtävien projekteista.

Riippuen käyttämästäsi NetBeansin versiosta, saatat joutua luomaan testejä varten sopivan hakemistorakenteen (ks. edellisen tehtävän hakemistorakenne), muuten NetBeans ei suostu luomaan projektiin testejä.

Tee seuraavat testit:

  • aloitetaan asiointi, koriin lisätään tuote, jota varastossa on ja suoritetaan ostos, eli kutsutaan metodia kaupan tilimaksu(), varmista että kutsutaan pankin metodia tilisiirto oikealla asiakkaalla, tilinumeroilla ja summalla
    • tämä siis on muuten copypaste esimerkistä, mutta verify:ssä on tarkastettava että parametreilla on oikeat arvot
  • aloitetaan asiointi, koriin lisätään kaksi eri tuotetta, joita varastossa on ja suoritetaan ostos, varmista että kutsutaan pankin metodia tilisiirto oikealla asiakkaalla, tilinumerolla ja summalla
  • aloitetaan asiointi, koriin lisätään kaksi samaa tuotetta, jota on varastossa tarpeeksi ja suoritetaan ostos, varmista että kutsutaan pankin metodia tilisiirto oikealla asiakkaalla, tilinumerolla ja summalla
  • aloitetaan asiointi, koriin lisätään tuote, jota on varastossa tarpeeksi ja tuote joka on loppu ja suoritetaan ostos, varmista että kutsutaan pankin metodia tilisiirto oikealla asiakkaalla, tilinumerolla ja summalla

Kaikkien testien tarkastukset onnistuvat mockiton verify-komennolla.

Muista, että kaikille testeille yhteiset alustukset on mahdollista tehdä metodissa, joka toistetaan ennen jokaista testiä:

Pankki pankki;
// ...

@Before
public void setUp() {
    pankki = mock(Pankki.class);
    // ...
}

4. Yksikkötestaus ja riippuvuudet: Mockito, osa 4

Jatketaan edellisen tehtävän koodin testaamista

  • varmista, että metodin aloitaAsiointi kutsuminen nollaa edellisen ostoksen tiedot (eli edellisen ostoksen hinta ei näy uuden ostoksen hinnassa), katso tarvittaessa apua projektin MockitoDemo testeistä!
  • varmista, että kauppa pyytää uuden viitenumeron jokaiselle maksutapahtumalle, katso tarvittaessa apua projektin MockitoDemo testeistä!

Kaikkien testien tarkastukset onnistuvat mockiton verify-komennolla.

Tarkasta viikoilla 1 ja 2 käytetyn JaCoCon avulla mikä on luokan Kauppa testauskattavuus.

Jotain taitaa puuttua. Lisää testi, joka nostaa kattavuuden noin sataan prosenttiin!

Mock-olioiden käytöstä

Mock-oliot saattoivat tuntua hieman monimutkaisilta edellisissä tehtävissä. Mockeilla on kuitenkin paikkansa. Jos testattavana olevan olion riippuvuutena oleva olio on monimutkainen, kuten esim. verkkokauppaesimerkissä luokka Pankki, kannattaa testattavana oleva olio testata ehdottomasti ilman todellisen riippuvuuden käyttöä testissä. Valeolion voi toki tehdä myös “käsin”, mutta tietyissä tilanteissa mock-kirjastoilla tehdyt mockit ovat käsin tehtyjä valeolioita kätevämpiä, erityisesti jos on syytä tarkastella testattavan olion riippuvuuksille tekemiä metodikutsuja.

5. Ostoskori TDD-tekniikalla

Jatketaan verkkokaupan parissa. Toteutuksen logiikka on periaatteiltaan hieman erilainen kuin aiemmissa tehtävissä käsittelemässämme verkkokaupassa. Tehtävän fokuksessa on kolme luokkaa Ostoskori, Ostos ja Tuote joiden suhde on seuraava:

Luokka Tuote on hyvin suoraviivainen. Tuotteesta tiedetään nimi, hinta ja varastosaldo:

public class Tuote  {
 
    private String nimi;
    private int hinta;
    private int saldo;
 
    public Tuote(String nimi, int hinta) {
        this.nimi = nimi;
        this.hinta = hinta;
    }
 
    public void setHinta(int hinta) {
        this.hinta = hinta;
    }
 
    public int getHinta() {
        return hinta;
    }
 
    public String getNimi() {
        return nimi;
    }
 
    public int getSaldo() {
        return saldo;
    }
 
    public void setSaldo(int saldo) {
        this.saldo = saldo;
    }
 
    @Override
    public String toString() {
        return nimi + " " + hinta + " euroa";
    }
}

Tuote siis kuvaa yhden tuotteen esim. Valion Plusmaito tiedot (nimi, hinta ja varastosaldo, tuotteella voisi olla myös esim. kuvaus ja muita sitä luonnehtivia kenttiä).

Ostoskoriin ei laiteta tuotteita vaan Ostoksia, ostos viittaa tuotteeseen ja kertoo kuinka monesta tuotteesta on kysymys. Eli jos ostetaan esim. 24 maitoa, tulee ostoskoriin Ostos-olio, joka viittaa maito-tuoteolioon sekä kertoo, että tuotetta on korissa 24 kpl. Ostos-luokan koodi:

public class Ostos {
 
    private int lkm;
    private Tuote tuote;
 
    public Ostos(Tuote tuote) {
        this.lkm = 1;
        this.tuote = tuote;
    }
 
    public int hinta() {
        return lkm * tuote.getHinta();
    }
 
    public int lukumaara() {
        return lkm;
    }
 
    public String tuotteenNimi() {
        return tuote.getNimi();
    }
 
    public void muutaLukumaaraa(int muutos) {
        lkm += muutos;
        if ( lkm<0 ) {
            lkm = 0;
        }
    }

    @Override
    public int hashCode() {
        return this.tuote.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
    
        Ostos other = (Ostos) obj;

        return this.tuote.equals(other.tuote);
    }
}

Tehtävänäsi on ohjelmoida luokka Ostoskori.

Ostoskorin API:n eli metodien rajapinnan tulee näyttää seuraavalta (metodeille on lisätty returnit jotta kääntäjä ei valittaisi koodista):

public class Ostoskori {
 
    public int tavaroitaKorissa() {
        // kertoo korissa olevien tavaroiden lukumäärän
        // jos koriin lisätty 2 kpl tuotetta "maito", 
        //   tulee metodin palauttaa 2 
        // jos korissa on 1 kpl tuotetta "maito" ja 1 kpl tuotetta "juusto", 
        //   tulee metodin palauttaa 2   

        return -1;
    }
 
    public int hinta() {
        // kertoo korissa olevien tuotteiden yhteenlasketun hinnan
 
        return -1;
    }
 
    public void lisaaTuote(Tuote lisattava) {
        // lisää tuotteen
    }
 
    public void poista(Tuote poistettava) {
        // poistaa tuotteen
    }
 
    public List<Ostos> ostokset() {
        // palauttaa listan jossa on korissa olevat ostokset
 
        return null;
    }
 
    public void tyhjenna() {
        // tyhjentää korin
    }
}

Kerrataan vielä: ostoskoriin lisätään Tuote-oliota metodilla lisaaTuote. Ostoskori ei kuitenkaan talleta sisäisesti tuotteita vaan Ostos-luokan oliota (jotka viittaavat tuotteseen):

Jos ostoskoriin laitetaan useampi kappale samaa tuotetta, päivitetään vastaavaa Ostos-oliota, joka muistaa kyseisen tuotteen lukumäärän.

Hae koodipohja kurssirepositorion hakemistossa koodi/viikko4/TddOstoskori

Ohjelmoi nyt ostoskori käyttäen Test Driven Development -tekniikkaa. Oikeaoppinen TDD etenee seuraavasti:

  • Kirjoitetaan testiä sen verran että testi ei mene läpi. Ei siis luoda heti kaikkia luokan tai metodin testejä, vaan edetään yksi testi kerrallaan.
  • Kirjoitetaan koodia sen verran, että testi saadaan menemään läpi. Ei yritetäkään heti kirjoittaa “lopullista” koodia.
  • Jos huomataan koodin rakenteen menneen huonoksi (eli havaitaan koodissa esimerkiksi toisteisuutta tai liian pitkiä metodeja) refaktoroidaan koodin rakenne paremmaksi, ja huolehditaan koko ajan, että testit menevät edelleen läpi. Refaktoroinnilla tarkoitetaan koodin sisäisen rakenteen muuttamista siten, että sen rajapinta ja toiminnallisuus säilyy muuttumattomana.
  • Jatketaan askeleesta 1

Tee seuraavat testit ja aina jokaisen testin jälkeen testin läpäisevä koodi. Jos haluat toimia oikean TDD:n hengessä, älä suunnittele koodiasi liikaa etukäteen, tee ainoastaan yksi askel kerrallaan ja paranna koodin rakennetta sitten kun koet sille tarvetta. Pidä kaikki testit koko ajan toimivina. Eli jos jokin muutos hajottaa testit, älä etene seuraavaan askeleeseen ennen kuin kaikki testit menevät taas läpi.

Luokkia Tuote ja Ostos ei tässä tehtävässä tarvitse muuttaa ollenkaan.

Tee seuraavat testit ja aina jokaisen testin jälkeen testin läpäisevä koodi

Lisää ja commitoi muutokset jokaisen vaiheen jälkeen GitHubiin, anna kuvaava commit-viesti.

1. Luodun ostoskorin hinta ja tavaroiden määrä on 0.

    // step 1
    @Test
    public void ostoskorinHintaJaTavaroidenMaaraAlussa() { 
        assertEquals(0, kori.hinta());
 
        // ...
    }

Laajenna testiä siten että se testaa myös tavaroiden määrän (metodin tavaroitaKorissa paluuarvo). Kun testi on valmis, ohjelmoi ostoskoria sen verran että testi menee läpi. Tee ainoastaan minimaalisin mahdollinen toteutus, jolla saat testin läpi.

Lisää ja commitoi muutokset ja anna kuvaava commit-viesti.

2. Yhden tuotteen lisäämisen jälkeen ostoskorissa on 1 tavara.

Huom: joudut siis luomaan testissäsi tuotteen jonka lisäät koriin:

    // step 2
    @Test
    public void yhdenTuotteenLisaamisenJalkeenKorissaYksiTuote() {
        Tuote maito = new Tuote("maito", 3);
 
        kori.lisaaTuote(maito);
 
        // ...
    }

Täsmennys: Vaikka metodin lisaaTuote parametrina on Tuote-olio, ostoskori ei tallenna tuotetta vaan luomansa Ostos-olion, joka “tietää” mistä tuotteesta on kysymys.

Lisää ja commitoi muutokset ja anna kuvaava commit-viesti.

3. Yhden tuotteen lisäämisen jälkeen ostoskorin hinta on sama kuin tuotteen hinta.

Lisää ja commitoi muutokset.

4. Kahden eri tuotteen lisäämisen jälkeen ostoskorissa on 2 tavaraa

Lisää ja commitoi muutokset.

5. Kahden eri tuotteen lisäämisen jälkeen ostoskorin hinta on sama kuin tuotteiden hintojen summa

Lisää ja commitoi muutokset.

6. Kahden saman tuotteen lisäämisen jälkeen ostoskorissa on 2 tavaraa

Lisää ja commitoi muutokset.

7. Kahden saman tuotteen lisäämisen jälkeen ostoskorin hinta on sama kuin 2 kertaa tuotteen hinta

Lisää ja commitoi muutokset.

8. Yhden tuotteen lisäämisen jälkeen ostoskori sisältää yhden ostoksen

tässä testataan ostoskorin metodia ostokset():

    // step 8
    @Test
    public void yhdenTuotteenLisaamisenJalkeenKorissaYksiOstosOlio() {
        kori.lisaaTuote(tuote1);
 
        List<Ostos> ostokset = kori.ostokset();
 
        // testaa että metodin palauttamin listan pituus 1
    }

Lisää ja commitoi muutokset.

9. Yhden tuotteen lisäämisen jälkeen ostoskori sisältää ostoksen, jolla sama nimi kuin tuotteella ja lukumäärä 1

Testin on siis tutkittava jälleen korin metodin ostokset palauttamaa listaa:

    // step 9
    @Test
    public void yhdenTuotteenLisaamisenKorissaYksiOstosOlioJollaOikeaTuotteenNimiJaMaara() {
        Tuote maito = new Tuote("maito", 3)
        kori.lisaaTuote(maito);
 
        Ostos ostos = kori.ostokset().get(0);
 
        // testaa täällä, että palautetun listan ensimmäinen ostos on halutunkaltainen.
    } 

Lisää ja commitoi muutokset.

10. Kahden eri tuotteen lisäämisen jälkeen ostoskori sisältää kaksi ostosta

Lisää ja commitoi muutokset.

11. Kahden saman tuotteen lisäämisen jälkeen ostoskori sisältää yhden ostoksen

eli jos korissa on jo ostos “maito” ja koriin lisätään sama tuote uudelleen, tulee tämän jälkeen korissa olla edelleen vain yksi ostos “maito”, lukumäärän tulee kuitenkin kasvaa kahteen.

Lisää ja commitoi muutokset.

12. Kahden saman tuotteen lisäämisen jälkeen ostoskori sisältää ostoksen jolla sama nimi kuin tuotteella ja lukumäärä 2

Lisää ja commitoi muutokset.

13. Jos korissa on kaksi samaa tuotetta ja toinen näistä poistetaan, jää koriin ostos jossa on tuotetta 1 kpl

Lisää ja commitoi muutokset.

14. Jos koriin on lisätty tuote ja sama tuote poistetaan, on kori tämän jälkeen tyhjä

tyhjä kori tarkoittanee että tuotteita ei ole, korin hinta on nolla ja ostoksien listan pituus nolla

Lisää ja commitoi muutokset.

15. Metodi tyhjenna tyhjentää korin

Lisää ja commitoi muutokset.

Jos ostoskorissasi on mukana jotain ylimääräistä, refaktoroi koodiasi niin että kaikki turha poistuu. Erityisesti ylimääräisistä oliomuuttujista kannattaa hankkiutua eroon, tarvitset luokalle vain yhden oliomuuttujan, kaikki ylimääräiset tekevät koodista sekavamman ja vaikeammin ylläpidettävän. Jos luokassasi on ylimääräisiä oliomuuttujia, poista ne. Varmista koko ajan testien avulla ettet riko mitään.

Lisää ja commitoi mahdolliset muutokset.

6. IntJoukon testaus ja siistiminen

Kurssirepositorion hakemistossa koodi/viikko4/IntJoukkoSovellus on aloittelevan ohjelmoijan ratkaisu syksyn 2011 Ohjelmoinnin jatkokurssin viikon 2 tehtävään 3.

Koodi jättää hieman toivomisen varaa sisäisen laatunsa suhteen. Refaktoroi luokan IntJoukko koodi mahdollisimman siistiksi

  • poista copypaste
  • anna muuttujille selkeät nimet
  • tee metodeista pienempiä ja hyvän koheesion omaavia

Koodissa on joukko yksikkötestejä, jotka helpottavat refaktorointia.

HUOM suorita refaktorointi mahdollisimman pienin askelin, pidä koodi koko ajan toimivana. Suorita testit jokaisen refaktorointiaskeleen jälkeen!

7. git: tägit (versionhallinta)

Tutustutaan tässä tehtävässä Gitin tageihin:

Git has the ability to tag specific points in history as being important. Typically people use this functionality to mark release points (v1.0, and so on)

Lue ensin http://git-scm.com/book/en/Git-Basics-Tagging (voit skipata kohdat ‘signed tags’ ja ‘verifying tags’)

Tee seuraavat samaan repositorioon, mihin palautat tehtäväsi:

  • tee tägi nimellä tagi1 (lightweight tag riittää)
  • tee kolme committia (eli 3 kertaa muutos + add + commit)
  • tee tägi nimellä tagi2
  • katso gitk-komennolla miltä historiasi näyttää
  • palaa tagi1:n aikaan, eli anna komento git checkout tagi1
    • varmista, että tagin jälkeisiä muutoksia ei näy
  • palaa nykyaikaan
    • tämä onnistuu komennolla git checkout main
  • lisää tägi edelliseen committiin
    • operaatio onnistuu komennolla git tag tagi1b HEAD^ , eli HEAD^ viittaa nykyistä “headia” eli olinpaikkaa historiassa edelliseen committiin
    • joissain windowseissa muoto HEAD^ ei toimi, sen sijasta voit käyttää muotoa HEAD~
    • tai katsomalla commitin tunniste (pitkä numerosarja) joko komennolla git log tai gitk:lla
  • kokeile molempia tapoja, tee niiden avulla kahteen edelliseen committiin tagit (tagi1a ja tagi1b)
  • katso komennolla gitk miltä historia näyttää

Tagit eivät mene automaattisesti etärepositorioihin. Pushaa koodisi githubiin siten, että myös tagit siirtyvät mukana. Katso ohje täältä

Varmista, että tagit siirtyvät GitHubiin:

Tehtävien palautus

Pushaa kaikki tekemäsi tehtävät (paitsi ne, joissa mainitaan, että tehtävää ei palauteta mihinkään) GitHubiin ja merkkaa tekemäsi tehtävät palautussovellukseen https://study.cs.helsinki.fi/stats/courses/ohtu-avoin-2022