Seuraava sivu : PyUnit ja Eclipse

PyUnit-opas (osa 1/2)

PyUnit on ohjelmien toistuvaan yksikkötestaukseen tarkoitettu sovelluskehys. PyUnit tuki löytyy valmiiksi suurimmasta osasta eri kehitysympäristöjä kuten Eclipse jne.

Goblinissa PyUnit:ia käytetään sekä opiskelijan koodin testaamiseen opettajan määrittelemillä testeillä, sekä opiskelijan koodin testaamiseen opiskelijan omilla testeillä. Tämän vuoksi tehtävät ratkaistakseen on osattava kirjoittaa PyUnit-testejä ja pystyttävä testaamaan näitä testejä omalla koneella jo ennen ratkaisun lähettämistä tarkastettavaksi.

PyUnit-testausluokan rakenne (PyUnit versio 2.7)

PyUnit tulee automaattisesti Pythonin mukana.

Itse luokkarunko, jonka sisään PyUnit testit kirjoitetaan ei sisällä mitään ihmeellistä. Ainoa asia on että luokan nimen tulee olla Test ja tiedoston, jossa tämä luokka on, nimen tulee olla test.py. Tämä vaatimus on vain Goblinia varten. Muutoin testiluokan ja tiedoston voi nimetä miten haluaa. Lisäksi luokan täytyy periä luokka unittest.TestCase (muista import unittest).

Testimetodit

Jokainen PyUnit testausluokka sisältää joukon yksikkötestejä, jotka testaavat toteutetun koodin eri ominaisuuksia. Testit kirjoitetaan testiluokan sisällä metodeihin. Jokaisen testimetodin nimen täytyy alkaa "test"

Yksikkötestin ideana on verrata ohjelmalta saatavaa dataa joihinkin oletettuihin arvoihin. Tämä tapahtuu kutsumalla unittest-moduulin TestCase-luokan metodeja assertXXX, missä XXX vaihtelee testin tyypistä riippuen. Yleisimmät metodit on esitetty taulussa alla. Assert metodi saa parametrinään tyypillisesti jonkin viestin joka esitetään jos tutkittu arvo eroaa oletetusta. Tämän lisäksi parametreina annetaan luonnollisesti ohjelmalta saatu ja oletettu arvo. Yhdessä metodissa voi olla vaikka kuinka monta assert-kutsua.

Joitakin assert-metodeja

Huomaa viimeisten parametrien järjestys!

  • assertEqual(arvo1, arvo2, viesti)
  • assertTrue(lauseke, viesti)
  • assertFalse(lauseke, viesti)
Metodit tulevat käyttöön perinnän kautta luokasta TestCase.

Mitä assertiot tekevät?

Jos testimetodin sisällä kutsutaan assert-metodia ja tulos ei ole halutunlainen, päättyy testimetodin suoritus automaattisesti ja testi merkitään epäonnistuneeksi. IDE:t kuten Eclipse näyttävät yleensä tällöin myös assertioon liitetyn viestin sekä oletetut ja löydetyt syötteet.

Aloitus ja lopetustehtävät

Testattava luokka täytyy muistaa tuoda mukaan import käskyllä.

Joissain tapauksissa testien kirjoittaja haluaa suorittaa jonkinlaisen alustuskoodin ennen jokaista testitapausta. Tämän saa PyUnit:illa aikaiseksi korvaamalla TestCase-luokan metodi setUp(). Vastaavasti jokaisen testin jälkeen voi tehdä automaattista siistintää korvaamalla metodin tearDown().

Testiluokan ajaminen

Lisäämällä testiluokan jälkeen tiedoston loppuun rivit:

if __name__ == "__main__":
    unittest.main()
testimoduulin voi suorittaa ja Python tekee testit automaattisesti.

Esimerkki

Alla on ohjelmalistaus luokasta, jota haluamme testata.

class Hello(object):

  def get_message(self):

     return "Hello World"


  def anna_viesti(self):

     return "Hei Maailma!"

Ohjelman pitäisi määritelmän mukaan antaa englanninkielisellä metodilla vastaukseksi merkkijono "Hello World!" ja suomenkielisellä metodinimellä vastaavasti "Hei Maailma". Kirjoitetaan tälle aiemmin opitun mukainen testiluokka:

import unittest

from hello_world import Hello

class Test(unittest.TestCase):


    # Creates the Hello-object being tested. 
    # You could do this in the test-methods as well.



    def setUp(self):

        self.hello = Hello()


    # Tests the english method for the string "Hello World!".

    def test_english(self):

        output = self.hello.get_message()

        self.assertEqual(output,
                      "Hello World!",
                      "get_message() does not output the correct value.")


    # Tests the finnish method for the string "Hei Maailma!".

    def test_finnish(self):

        output = self.hello.anna_viesti()

        self.assertEqual(output,
                         "Hei Maailma!",
                         "hae_viesti() does not output the correct value.")

if __name__ == "__main__":
    unittest.main()


Jos nyt ajamme ylläolevan testin vaikkapa Eclipsen kautta (run as -> Python unit-test) huomaamme, että englanninkielinen testi ei mennyt läpi, koska merkkijonon lopussa ollut huutomerkki puuttui. Voimme nyt korjata koodissa olevan virheen ja ajaa testin uudelleen, jolloin virheen ei pitäisi toivottavasti enää toistua. Saattaa kuitenkin olla että virheen korjauksesta johtuen jokin toinen osa koodia toimii nyt väärin - Joku oli saattanut vaikkapa lisätä muualla koodissa puuttuvan huutomerkin metodilta saamansa merkkijonon perään jolloin tuo koodi tuottaa nyt kaksi huutomerkkiä. Jos testisetti on kattava, löytyisi tämäkin virhe välittömästi seuraavan testauksen aikana.

Todellisempaa testausta

Ylläoleva esimerkki ei ollut kovin mielenkiintoinen, koska merkkijonot eivät syntyneet juuri minkäänlaisen koodin tuloksena. Normaalisti testitapaukset ovat kuin peruskoulun pistokokeita. Opettaja ei voi testeillä varmistaa että oppilaat todella osaisivat taivuttaa kaikki opiskeltavan kielen verbit oikeisiin muotoihin, sillä ainoa tyhjentävä vastaus olisi kirjoittaa kielioppisäännöt, joilla verbit taivutetaan. Tämän sijaan opettaja (tai meidän tapauksessamme koodaaja) pystyy hyvin helposti keksimään muutaman verbin ja sen miltä niiden pitäisi eri muodoissaan näyttää.

Yksikkötesti siis on tietynlainen pistokoe ohjelmalle - testiin kirjoitetaan muutamia tehtäviä ohjelmalle tehdä ja oletettuja tuloksia testien vastineiksi.

PyUnit Eclipsessä

Uudemmissa Eclipse IDE:n versioissa on joukko sisäänrakennettuja PyUnit-ominaisuuksia, jotka helpottavat vielä huomattavasti testien kirjoittamista ja suorittamista. Katsotaan seuraavaksi miten testaus olisi voitu suorittaa Eclipsessä.

Seuraava sivu : PuUnit ja Eclipse

Jos et ole käyttänyt Eclipseä aiemmin ja haluat nyt siirtyä siihen, niin käy lukemassa Ohjelmoinnin peruskurssin Eclipse-opas.

PyUnit unix/dos ympäristössä

Aja testiluokka normaalin Python-ohjelman tapaan:
login:> python test.py
test_english (__main__.Test) ... FAIL
test_finnish (__main__.Test) ... ok

======================================================================
FAIL: test_english (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test.py", line 26, in test_english
    "get_message() does not output the correct value.")
AssertionError: get_message() does not output the correct value.

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)