• Ei tuloksia

Automatisoitu testaus Drupal 7:n moduulien kehittämisessä

N/A
N/A
Info
Lataa
Protected

Academic year: 2022

Jaa "Automatisoitu testaus Drupal 7:n moduulien kehittämisessä"

Copied!
128
0
0

Kokoteksti

(1)

Automatisoitu testaus Drupal 7:n moduulien kehittämisessä

Perttu Helle

Opinnäytetyö

Tietojenkäsittelyn koulutusohjelma 2011

(2)

Tiivistelmä

2.11.2011 Tietojenkäsittelyn koulutusohjelma

Tekijä

Perttu Helle Ryhmätunnus

TIKO08SI Raportin nimi

Automatisoitu testaus Drupal 7:n moduulien kehittämisessä Sivu- ja lii- tesivumäärä 91 + 34 Opettajat tai ohjaajat

Juhani Välimäki

Tässä opinnäytetyössä tutkitaan testivetoista ohjelmistokehitystä. Työn päätavoite oli tutkia Test Driven Development -menetelmää ja PHPUnit-testauskehystä sekä tutkia testivetoisen ohjelmistokehityksen mahdollisuuksia verkkojulkaisujärjestelmä Drupal 7:ssa.

Opinnäytetyössä on teoriaosuus ja empiirinen osuus. Teoriaosassa tutkittiin Test Dri- ven Development -menetelmää ja sen soveltamiseen perustuvan PHPUnit-

testauskehyksen käyttöä. Empiirisessä osassa käsiteltiin Drupal 7 moduulien ohjelmoin- tia. Opinnäytetyön aineisto perustuu kirjallisuuteen sekä internet-lähteisiin.

Teoriaa tutkittaessa selvisi, että Test Driven Development -menetelmän käyttö on mahdollista PHP-kielisten sovellusten ohjelmoinnissa. Vaihtoehtoisia testauskehyksiä on useita. Opinnäytetyössä havaittiin myös, että vaihtoehtoisista testauskehyksistä SimpleTest-testauskehys saa Drupal 7:ssa parhaan tuen. Drupal 7:ään kuuluva Testing- moduuli perustuu SimpleTest-testauskehykseen.

Empiirisessä osuudessa havaittiin, että testivetoisessa kehityksessä ohjelmointi testien kanssa kestää pidempään, mutta nopeuttaa mahdollisten uusien virheiden löytämistä ohjelmistoa edelleen kehitettäessä.

Empiirisen osan perusteella Test Driven Development -menetelmän käytöllä, automa- tisoitujen testien ohjelmoinnilla ja Drupal 7:aan kuuluvan Testing-moduulin hyödyntä- misellä näyttää olevan selviä etuja. Vaikka opinnäytetyössä toteutettiin vain yksi Drupal 7 moduuli, tulokset näyttävät niin selviltä, että tutkimuksen perusteella voidaan testive- toista Drupal 7 -kehitystä suositella yleisemminkin.

Opinnäytetyö tehtiin syksyllä 2011.

Asiasanat

Ohjelmointi, PHP, verkko-ohjelmointi, avoin lähdekoodi

(3)

Abstract

2.11.2011 Business Information Technology

Author or authors

Perttu Helle Group

TIKO08SI The title of thesis

Automatic testing in Drupal 7 module development Number of pages and ap- pendices 91 + 34 Supervisor or supervisors

Juhani Välimäki

This Bachelor’s thesis discusses test driven software development. The primary goal of the study was to examine the Test Driven Development method and the PHPUnit testing framework to provide a test based development method for developing mod- ules for a content management system named Drupal 7.

The thesis includes a theory section and an empirical section. The theory section dis- cusses the Test Driven Development and how to implement it with the PHPUnit test- ing framework. The empirical part deals with programming a software module to Drupal 7. The study was based on literature and internet sources.

The study showed that it is possible to implement the Test Driven Development method in developing software in PHP programming language. There is more than one framework to choose from. It was also found out that of several frameworks avail- able, the SimpleTest- framework based Testing module is best supported in Drupal 7.

The empirical part showed that developing tests takes more time compared to writing the actual program only, but it also provides a more secure way to change software since new possible errors are found more quickly.

The empirical part gave a clear image on the benefits of implementing Test Driven Development and using a Testing module with automated tests for developing soft- ware for Drupal 7. Despite the fact that this study covered developing only one mod- ule for Drupal, the benefits seemed to be so obvious that one can recommend this method for developing software for Drupal 7 in general.

Key words

Programming, PHP, web programming, open source

(4)

Sisällys

Sisällys ...1  

Symboliluettelo ja määritelmät ...3  

1   Johdanto...8  

1.1   Opinnäytetyön tavoitteet ...10  

1.2   Tutkimusongelmat ja rajaukset...11  

1.3   Metodologia ...11  

1.4   Opinnäytetyöraportin rakenne ...12  

2   Test Driven Development -ohjelmistokehitysmenetelmä...13  

2.1   Vaatimukset testeiksi ja ensimmäinen testiajo ...16  

2.2   Ohjelmoi testien läpäisemiseksi ja vain testien läpäisemiseksi ...17  

2.3   Koodin refaktorointi...18  

2.3.1   Päällekkäisyyden poistaminen ...19  

2.3.2   Koodin tarkoituksen selkiyttäminen ...21  

2.3.3   ”Haisevan koodin” siivoaminen ...23  

2.3.4   Muut ohjelmakoodin ongelmat...24  

2.3.5   Refaktoroinnin periaatteita ...28  

2.4   Tuottavuuden muutos ...30  

3   PHPUnit-ohjelmointikehys...32  

3.1   Yksikkötesti...34  

3.2   Toiminnallinen testi ...35  

3.3   Testien ohjelmointi PHPUnit-kehyksessä...36  

3.4   Testiajo komentotulkissa...37  

3.5   Testien organisoiminen ja ajojärjestys...38  

3.6   Testaaminen kahdennetuin olioin...40  

3.7   Stub-olion käyttö...42  

3.8   Mock-olion käyttö...44  

3.9   PHPUnitin muu tehokäyttö...46  

4   Taustateorian soveltaminen Drupal-viitekehykseen...49  

4.1   Tavoite, ongelmat, kehittämistehtävä...49  

4.2   Menetelmävalinnat ja suunnitelmakuvaus ...49  

4.3   Työtapakuvaus...51  

(5)

4.4   Drupal-verkkojulkaisujärjestelmä...52  

4.4.1   Drupalin arkkitehtuuri...54  

4.4.2   Drupalin alijärjestelmät ja moduulin ohjelmointi ...57  

4.5   Drupal-moduulin toteutus ...58  

4.6   Testaaminen Drupalissa ...64  

4.6.1   Testin ohjelmointi ...65  

4.6.2   Testien suorittaminen ...68  

4.6.3   Drupal-ohjelmakoodin yksikkötestit ...70  

5   Produkti...72  

5.1   Moduulin toteuttaminen ...73  

5.1.1   Testien suunnittelu...73  

5.1.2   Moduulin ohjelmointi...75  

5.1.3   Yksikkötestit ja toiminnalliset testit...76  

6   Pohdinta ...78  

6.1   Tulosten tarkastelu omilla tulkinnoilla...78  

6.2   Tulosten hyödyntäminen ...80  

6.3   Opinnäytetyöprosessin ja oman oppimisen arviointi ...81  

6.4   Kehittämis- ja jatkotutkimusehdotukset...82  

Lähteet ...84  

Liitteet ...92  

Liite 1.   Drupal-kehitysympäristön asennus ...92  

Liite 2.   Moduulin ohjelmakoodit...100  

Liite 3.   Viimeinen testiajoraportti ...125  

(6)

Symboliluettelo ja määritelmät

Avoin lähdekoodi, Open source

Ohjelmistokehityksen periaate, jonka mukaan käyttäjien tulee voida vapaasti jakaa ohjelmistoa, tutustua sen ohjelmakoodiin ja halutessaan pystyä muuttamaan ohjelmaa itse ja jakaa muu- tettua versiota eteenpäin. (Open Source Initiative.)

CMS, Content Management System, verkkojulkaisujärjestelmä

Palvelimella suoritettava verkkosivujen julkaisuohjelmisto, jo- ka tarjoaa helpon keinon tietokantapohjaisen verkkosivuston rakentamiseen, pystyttämiseen ja ylläpitoon. (Typo3 Asso- ciation 2011.)

CSS, Cascading Style Sheet

Ohjelmointikieli, jolla HTML- tai XML-kielellä ilmaistulle tiedostolle voidaan antaa haluttu ulkoasu. CSS on yleisimmin käytössä verkkosivustoilla. CSS-säännöillä voidaan määritellä esimerkiksi erilaisten elementtien kokoa, asettelua, värejä ja näkyvyyttä. (W3C 2010.)

Drupal Avoimen lähdekoodin verkkojulkaisujärjestelmä, joka on to- teutettu PHP-ohjelmointikielellä ja käyttää jotakin tietokantaa tietovarastona. Yleisimmin toteutettu LAMP-ympäristöön.

Ensimmäinen versio julkaistiin vuonna 2001. (Drupal.org 2011a; Drupal.org 2011b.)

Funktio ks. metodi

GPL, GNU General Public License

Avoimen lähdekoodin ohjelmistojen lisenssi. GPL-lisensoitua ohjelmaa, kuten esimerkiksi Drupalia saa käyttää ja muokata vapaasti, mutta muokatulla versiolla tulee olla sama lisenssi, muutokset tulee merkitä selvästi (kuten esimerkiksi READ-

(7)

ME.txt -tiedostoon) ja alkuperäisen tekijän tulee olla mainit- tuna. (Free Software Foundation 2010.)

HTML, HyperText Markup Language

Verkkosivuilla käytettyjen kuvauskieli eli verkkosivujen oh- jelmointikieli, jolla voidaan esittää verkkosivujen sisältö ja ra- kenne. HTML-tekstiin voidaan sisällyttää upottaa esimerkiksi tekstiä, taulukoita, hyperlinkkejä ja kuvia sekä lomakkeita ja multimediaa. (W3C 2010.)

LAMP Verkkosivujen julkaisuun käytetty palvelinohjelmistojen ko- koonpano, joka koostuu [L] Linux-käyttöjärjestelmästä, [A]

Apache-verkkopalvelinohjelmistosta, [M] MySQL- tietokantaohjelmistosta ja [P] PHP-, Perl tai Python - ohjelmointikielestä (Dougherty 2001).

Luokka Olio-ohjelmoinnissa olion rakennemalli eli pohjapiirustus.

Luokasta voi toteuttaa useita olioita eli luokan ilmentymiä.

Luokassa voi olla muuttujia ja metodeita ja luokka voi periy- tyä toisesta luokasta. (Software Design Consultants LLC 1999.)

Metodi, funktio Yksittäisen ohjelmoidun loogisen toiminnan sisältävä funktio.

Olio-ohjelmoinnissa olion toiminnallisuudet sijaitsevat luok- kien metodeissa. (Software Design Consultants LLC 1999.) Mock-olio, mock-object, valeolio

Ohjelmistotestin ajaksi luotu väliaikainen olio, jonka tarkoi- tuksena on imitoida testattavalle luokalle korvatun olion toi- mintaa. Mock-oliossa metodin logiikka voidaan korvata kiin- teillä palautusarvoilla. (Astels 2003, 145-148.)

MVC, Model-View-Controller

Suunnittelumalli, jossa ohjelmiston toiminnan osat erotetaan toisistaan eri luokiksi. Model -osa vastaa tietojen varastoinnis-

(8)

ta, View-osa niiden esittämisestä ja Controller tapahtumankä- sittelystä eli ohjelman toiminnoista. (eNode, Inc. 2002.) MySQL Maailman käytetyin avoimen lähdekoodin tietokantaohjelmis-

to. MySQL toimii lähes kaikissa suosituimmissa palvelinkäyt- töjärjestelmissä. (Oracle Corporation 2010.)

Olio, Object Luokasta eli olion mallista tehty ilmentymä. Olio voi sisältää tietoa ja toiminnallisuutta (Software Design Consultants LLC 1999).

PDO, PHP Data Objects

Tietokantojen hallintaan tehty PHP:n laajennus, joka käyttää olio-ohjelmointimallia (Php.net 2011a).

PHP, PHP eli Hypertext Preprocessor

HTML:n sisään upotettavissa oleva skriptaus- eli ohjelmointi- kieli. PHP:ssä on piirteitä C-, Java- ja Perl-ohjelmointikielistä ja sen tarkoituksena on auttaa web-ohjelmoijia toteuttamaan dynaamisesti muuttuvia verkkosivuja. (Php.net 2011b.) PHPUnit Ohjelmistokirjasto, jolla pystytään ohjelmoimaan automatisoi-

tuja testejä PHP-kielisen ohjelmiston kehitystyössä (Berg- mann, S. 2005-2011a).

Rajapinta, Application Programming Interface, API

Joukko sääntöjä, joita noudattamalla voidaan siirtää tietoa oh- jelmien, käyttöjärjestelmien ja käyttäjien välillä. Graafinen käyttöliittymä on esimerkki rajanpinnasta, jonka välityksellä käyttäjä kommunikoi tietokoneen kanssa. (PCMag.com 2011.) RSS, Really Simple Syncication, uutissyöte

XML-muotoinen tapa siirtää tietoa. Palveluntarjoaja voi tarjo- ta haluamaansa tietoa RSS-syötteenä, jonka voi tilata itselleen selaimeen, erilliseen RSS-syötteiden lukuohjelmaan tai sähkö- postiohjelmaan. (Rowse 2010.)

(9)

SQL, Structured Query Language

Relaatiotietokantojen hallintaan tarkoitettu ohjelmointikieli.

SQL on perustasolla käytännössä yhteensopiva, mutta prose- duurien ja funktioiden toteutuksessa on tuotekohtaisia eroja (Hovi 2004, 2-5). Tunnettuja SQL:iä tukevia tuotteita ovat avoimen lähdekoodin ohjelmistoista MySQL, PostgreSQL ja uudehko MariaDB sekä suljetun lähdekoodin ohjelmistoista Oraclen SQL, IBM:n DB2, Microsoftin SQL Server ja MSOf- fice-pakettiin kuuluva Access.

Stub-olio, stub-object, tynkäolio

Testiä varten perustettu tyhjä väliaikainen luokka, jonka tar- koitus on estää testiajon kaatuminen kääntäjän virheeseen vii- tatun alkuperäisen luokan puuttumisen vuoksi (Astels 2003, 169).

System under test, SUT Testattavana oleva ohjelmistokokonaisuus (Meszaros 2003- 2008).

Test double, kahdennettu olio

Testauksessa käytetty menetelmä luoda testiä varten alkupe- räisen olion toimintaa matkiva testin aikainen olio (Bergmann 2005-2011e). Ks. myös mock-object ja stub-object.

Test Driven Development, TDD

Ohjelmistokehitysmenetelmä, jonka keskeisiä elementtejä ovat testien kirjoittaminen, kehitystyön jakaminen mahdolli- simman pieniin osiin ja automatisoitujen testien luominen en- nen varsinaista ohjelmakoodia. Menetelmää noudattaen oh- jelmakoodia kirjoitetaan vain testejä varten ja testien läpäise- miseksi. (Koskela 2008, 4.)

Testit edellä -ohjelmointi

Ohjelmistokehitysmenetelmä, jossa ohjelmointi alkaa testien

(10)

kirjoittamisella ja jatkuu vasta testien valmistuttua varsinaisen ohjelmakoodin kirjoittamisella (Ambler 2002-2011).

Toiminnallinen testi, functional test

Ohjelmoijan tekemä testi, joka testaa ohjelmiston toimintaa loppukäyttäjän kannalta (Bergmann 2005-2011i).

XML, Extensible Markup Language

Joustava tekstimuotoinen tapa tallentaa tai siirtää tietoa eri jär- jestelmien välillä (W3C 2011).

Yksikkötesti, unit test Ohjelmoijan tekemä testi, joka testaa ohjelmistosta mahdolli- simman pienen toiminnallisen yksikön toiminnan nopeasti ja helposti (Ambler 2002-2011).

(11)

1 Johdanto

Internet-sivustojen kehityksessä on tapahtunut suuri murros viimeisten 5-10 vuoden aikana. Aiemmin verkkosivustoja tehtiin joko staattisina eli muuttumattomina HTML- sivuina tai isojen ja kalliiden yritysten valmistamien julkaisujärjestelmien avulla. Avoi- men lähdekoodin julkaisujärjestelmiä ei ollut ja tai ne olivat käytettävyydeltään ja toi- minnoiltaan heikkoja. Vähitellen tekniikka on kehittynyt ja verkkosivujen valmistukseen on tullut huomattavasti lisää vaihtoehtoja sekä kaupallisina sovelluksina että avoimen lähdekoodin ohjelmistoina.

Avoimen lähdekoodin julkaisujärjestelmiä on määritelmästä riippuen kymmeniä tai sa- toja, joten valinnanvaraa alkaa olla paljon (The CMS Matrix). Osa julkaisujärjestelmistä on suunniteltu tiettyyn tarkoitukseen, kuten blogin tai uutisten julkaisuun, toiset ensisi- jaisesti esimerkiksi kuvagallerioiksi. Osa julkaisujärjestelmistä on kehittynyt erittäin mo- nipuolisiksi ja niitä voi käyttää hyvin monentyyppisten ja monipuolisten sivustojen jul- kaisuun. Eniten käytetyt julkaisujärjestelmät ovat korkealaatuisia ja kehittyvät versio versiolta vakaammiksi ja helppokäyttöisemmiksi (Boye 2011; Kas 2009). Julkaisujärjes- telmien määrän kasvaessa ja niiden monipuolistuessa muillakin kuin ohjelmointitaitoi- silla kehittäjillä alkaa olla riittävästi vaihtoehtoja.

Käytetyimmillä julkaisujärjestelmillä on toteutettu kymmeniä miljoonia verkkosivustoja ja niihin on saatavana tuhansia erilaisia laajennuksia (Drupal.org 2011a; Neal 2011;

Wordpress.org 2011). Avoimen lähdekoodin julkaisujärjestelmät ovat erittäin kilpailu- kykyisiä ja niitä käytetään kaikenkokoisten sivustojen järjestelminä. Isoja ja tunnettuja esimerkkejä niillä toteutetuista sivustoista ovat mm. New York Times

http://www.nytimes.com, Suomi24:n keskustelualueet http://keskustelu.suomi24.fi ja Yhdysvaltojen Valkoisen Talon sivusto The White House http://www.whitehouse.gov (Dalziel 2010; Lahti 2010; Wordpress.org 2011).

Julkaisujärjestelmien muokattavuus ja laajennettavuus perustuu usein modulaariseen rakenteeseen, jonka avulla järjestelmän toimintaan voidaan vaikuttaa rajapintojen kaut- ta. Rajapintojen käyttöä laajennusten ohjelmoinnissa on ohjeistettu joskus hyvinkin havainnollisesti (Drupal.org 2011c; Drupal.org 2011d). Laajennukset hyödyntävät jul- kaisujärjestelmän rajapintoja ja osassa laajennuksista on omia rajapintoja muille laajen- nuksille.

(12)

Julkaisujärjestelmän toimintojen lisääntyessä ohjelmakoodin määrä kasvaa ja kokonai- suus muuttuu vähitellen yhä monimutkaisemmaksi. Samalla korjausten tai kehitystyön yhteydessä syntyvien virheiden todennäköisyys kasvaa. Ohjelmointiprosessin tukena onkin tärkeää käyttää helppoa ja nopeaa testausmenetelmää, jolla voidaan varmistua muutetun tai lisätyn ohjelmakoodin toimivuudesta, yhteensopivuudesta ja virheettö- myydestä. Menetelmä ohjaa ohjelmakoodin kirjoittamista rakenteeltaan selkeäksi, hel- posti luettavaksi ja itse itsensä selittäväksi. Selkeys helpottaa ohjelmakoodin pariin pa- laamista ja sen turvallista muuttamista. Sotkuinen, monimutkainen ja epäloogisesti pit- kissä kokonaisuuksissa toteutettu ohjelmakoodi on jo itsessään riski, koska sen osien muuttaminen voi rikkoa odottamattomalla tavalla jotakin aivan eri puolella ohjelmistoa.

Ongelmien välttämiseksi ja ohjelmakoodin testaamiseksi ohjelmistoteollisuudessa on kehitetty erilaisia menetelmiä. Perinteisessä vesiputousmallissa testaus tehdään usein ohjelmakoodin kirjoittamisen jälkeen ja testaaja on eri henkilö kuin ohjelmoija. Testaaja joutuu aluksi tutustumaan itselleen vieraaseen ohjelmakoodiin, eikä testaus välttämättä kata kaikkea ohjelmakoodia. Niin sanotuissa testit edellä -metodologioissa testaus on siirretty osaksi ohjelmistokehitysprosessia eli ohjelmoijan vastuulle. Ohjelmoija voi varmistua paremmin työnsä jatkuvasta laadusta ja virheettömyydestä, kun hän testaa itse ohjelmakoodiaan sen kirjoittamisen aikana. Kun testit tehdään ohjelmoinnin rinnal- la, testit kattavat koko kirjoitetun ohjelmakoodin. Ohjelmakoodin testaamisen kynnyk- sen ollessa mahdollisimman matala voi ohjelmoija voi myös turvallisemmin refaktoroi- da eli korjata ohjelmakoodia rakenteellisesti paremmaksi. Refaktoroimalla ohjelmakoo- dista tulee korkealaatuisempaa ja luettavampaa sen toimiessa ulkoisesti edelleen alkupe- räisen koodin tavalla. (Ambler 2002-1011; Astels 2003, 42; Murphy 2005.)

Test Driven Development (TDD) on ohjelmistokehitysmenetelmä, joka tarjoaa työka- lut ja prosessin ohjelmoinnin laadun parantamiseen ohjaamalla ohjelmistokehittäjät miettimään ensin ohjelmiston toiminnallisia vaatimuksia, ohjelmoimaan ensin testita- paukset eli määrittelemällä "mitä ohjelmiston pitää tehdä ja mistä myös tiedän että se toimii halutusti" ja lopuksi toteuttamaan halutut toiminnallisuudet ohjelmaan. Astels (2003, 15) painottaa erityisesti prosessin järjestystä. Myös Koskela (2008, 7-9) korostaa vaatimusten suunnittelussa toiminnallisten vaatimusten tarkastelua aluksi ja ohjelma- koodin rakenteen suunnittelua viimeiseksi. Ensin suunnitellaan mitä ohjelman pitää tehdä ja vasta sen jälkeen miten se tehdään. Test Driven Development -menetelmän

(13)

sivutuotteena syntyy lisäksi pysyvää testauskoodia, jolla voidaan myöhemmin testata ohjelmiston muutosten yhteydessä sen virheettömyys ja mahdollisten rajapintojen muuttumattomuus. Menetelmän sovellukset on toteutettu ensimmäisenä Java- ohjelmointikielellä. Testit edellä -menetelmää varten tehty JUnit-testauskehys on muunnettu monelle muullekin ohjelmointikielelle sen ilmeisten etujen vuoksi. PHP- kielisessä ympäristössä vastaava testauskehys on nimeltään PHPUnit (Bergmann 2005).

PHPUnitin lisäksi PHP-sovellusten testaamiseksi on tehty SimpleTest-testauskehys (Penet 2005).

Drupal on monipuolinen julkaisujärjestelmä, jota on helppoa laajentaa eri käyttötarkoi- tuksiin. Järjestelmän käyttö ja ylläpito on helppoa eivätkä edellytä ohjelmointitaitoja tai HTML-koodin tuntemusta (The University of Arizona). Järjestelmää voi laajentaa tu- hansilla valmiilla laajennuksilla tai ulkoasua muuntaa sadoilla ilmaisilla teemoilla. Tarvit- taessa Drupaliin voi toteuttaa varsin pienellä vaivalla räätälöidyn laajennuksen (Dru- pal.org 2011d). Oman yksilöllisen verkkosivun ilmeen toteuttaminen on hyvinkin no- peaa, koska käyttöönottovalmiita ulkoasupohjia on runsaasti (Drupal.org 2011e). Oh- jelmoijalle Drupal tarjoaa kattavasti dokumentoidut rajapinnat, joiden avulla järjestel- män toimintaa voidaan muuttaa monella tavalla (Drupal.org 2011f). Rajapintojen avulla järjestelmän toimintaan voidaan kytkeä erilaisia palveluita tai tietovarastoja. Drupal voi- daan ottaa käyttöön myös sellaisessa sivustossa, jossa korkean käytettävyyden vaatimus edellyttää useamman verkko- tai tietokantapalvelimen klusteria (Quinn 2007).

Drupal on toteutettu PHP-ohjelmointikielellä, joten tässä tutkimuksessa syvennytään Test Driven Development -menetelmään, PHPUnit-testauskehykseen sekä produktin aikana Drupalin omaan SimpleTest-testauskehyksestä sovitetun Testing-moduulin käyt- töön.

1.1 Opinnäytetyön tavoitteet

Opinnäytetyön päätavoitteena on selvittää Test Driven Development -

ohjelmistokehitysmenetelmää ja PHPUnit -ohjelmistokehyksen käyttöä sekä soveltaa löydettyä tietoa Drupal 7:n ohjelmoinnissa. Teoriaosan tavoitteena on selvittää Test Driven Development -menetelmän ja PHPUnit-testauskehyksen mahdollisia etuja ja haittoja perinteiseen ohjelmistokehitysmenetelmään verrattuna.

(14)

Toisen vaiheen eli produktiosan tavoitteena on toteuttaa Drupal 7-

verkkojulkaisujärjestelmään laajennus eli moduuli ohjelmoiden moduuli automaattisten testien kautta ja jäsentää testausvetoista kehitysmenetelmää Drupal-viitekehyksessä.

Automaattiset testit on tarkoitus jättää osaksi moduulia niin, että automatisoiduilla tes- teillä pystytään varmistumaan ohjelmakoodin toimivuudesta mahdollisten moduulin laajentamisen, muutosten ja korjausten yhteydessä.

Opinnäytetyön toimeksiantaja, opiskelijan oma yritys Itusi Oy käyttää Drupal-

järjestelmää verkkosivustojen julkaisualustana ja opinnäytetyön osana tuotettava laajen- nus tullaan ottamaan käyttöön osassa yrityksen ylläpitämistä verkkopalveluista. Opin- näytetyön tavoitteena on laajentaa yrityksen käytettävissä olevaa Drupal-osaamista tes- tivetoisella kehitysmenetelmällä tuotettavien moduulien tuotantoon.

1.2 Tutkimusongelmat ja rajaukset

Keskeisiä tutkimuskysymyksiä ovat seuraavat: Mitä etua ohjelmistoteollisuudessa on Test Driven Development -menetelmän soveltamisesta? Miksi aikaa eli resursseja vievä testien kirjoittaminen on kannattavaa? Miten Test Driven Development -menetelmää voidaan soveltaa PHP-kielisen ohjelmiston tuotannossa? Miten Drupal 7:ään ohjelmoi- daan moduuleita testivetoisella kehitysmenetelmällä? Voidaanko Drupal 7 -

viitekehyksessä soveltaa tutkittuja menetelmiä ja ohjelmointikehyksiä?

1.3 Metodologia

Tutkimus jakautuu toteutettu kirjallisuustutkimukseen ja teoriaa soveltavaan produk- tiosaan. Kirjallisuustutkimuksella selvitetään Test Driven Development -

ohjelmistokehitysmenetelmän periaatteita sekä PHPUnit -ohjelmistokehyksen sovelta- mista yleisesti.

Produktiosassa tutustutaan Drupal 7 -julkaisujärjestelmään sekä sen moduulien ohjel- mointiin soveltaen testivetoista ohjelmistokehitysmenetelmää. Osana tutkimusta oh- jelmoidaan Drupal 7 moduuli, jossa sovelletaan tutkittua teoriaa. Soveltavan osan tar- koituksena on arvioida teorian soveltuvuutta Drupal 7 -viitekehyksessä.

(15)

1.4 Opinnäytetyöraportin rakenne

Opinnäytetyö alkaa aiheeseen liittyvän keskeisen termistön määrittelyllä eli symboliluet- telolla. Johdannossa perustellaan opinnäytetyön tärkeys ja ajankohtaisuus, asetetaan tavoitteet ja määritellään tutkimusongelmat. Toisessa ja kolmannessa kappaleessa tutki- taan testivetoisen ohjelmistokehityksen menetelmän teoriaa ja PHP-kielistä ohjelmoin- tikehystä. Teoriaa tutkitaan Test Driven Development -kautta ja ohjelmointikehystä tutkitaan PHPUnit-ohjelmointikirjaston avulla. Neljännessä kappaleessa määritellään produktiosa ja selvitetään Drupal-ohjelmoinnin ja automaattisen testaamisen työkaluja tässä viitekehyksessä. Viidennessä kappaleessa käsitellään produktin ohjelmointia ja erityispiirteitä sekä arvioidaan produktion tuotannossa saatuja teorian soveltamisen tu- loksia. Kuudennessa kappaleessa analysoidaan saatuja tuloksia, nostetaan esiin kehittä- miskohteita tai jatkotutkimuksen aiheita sekä arvioidaan opinnäytetyöprossia ja opiske- lijan omaa oppimista.

Liitteenä on ohjeistus ohjelmointia helpottavan NetBeans 7.0 IDE -

ohjelmointiympäristön muokkaamiseksi Drupalin ohjelmointia varten, produktiosassa tuotetun moduulin tiedostojen ohjelmakoodit sekä ruutukaappaus viimeisen testiajon lopputuloksesta eli viiden testiluokan ja niiden sisältämien kaikkiaan 325 onnistuneen testitapauksen raportista.

(16)

2 Test Driven Development -ohjelmistokehitysmenetelmä

Ohjelmistoteollisuudessa ohjelmiston laatu ei aina vastaa odotuksia. Laadun valvonta on suorassa yhteydessä ohjelmiston testaamiseen ja testien kattavuuteen. Jos testaami- nen tehdään ohjelmistokehityksen viimeisessä vaiheessa, sen tekee usein varsinaista koodia tuntematon ohjelmoija, jonka täytyy ensin tutustua testattavaan koodiin. Toisen kirjoittaman koodin tarkistaminen on ihmiselle joskus lähes mahdotonta, jos ohjelmis- ton arkkitehtuuri on epäselvä ja koodi monimutkaista (Ojanperä 2011). Astels (2003, 5) toteaa, että testit saatetaan suunnitella ohjelman määrittelydokumentin perusteella, jon- ka seurauksena osa ohjelmakoodista voi jäädä testien ulkopuolelle. Kun ohjelmistoa täytyy jossakin vaiheessa korjata tai muuttaa, voi tämä aiheuttaa vaillinaisesti testien piirissä olevassa ohjelmistossa odottamattomia ja vaikeasti paikannettavissa olevia vir- heitä.

Test Driven Development -menetelmää voidaan käyttää oliomallia tukevissa ohjel- mointikielissä. Menetelmässä luokkien ja metodien testaaminen on osa ohjelmointipro- sessia. Testaussykli on erittäin lyhyt ja kerralla kirjoitettavan ohjelmakoodin määrä on hyvin pieni. Testaaminen on siis keskeinen osa ohjelmointiprosessia ja ohjaa ohjelmis- ton suunnittelua. Testit säilytetään osana ohjelmistoa eikä menetelmän periaatteiden mukaan tuotettua ohjelmistoa oteta tuotantokäyttöön, jos siihen ei ole liitettynä katta- vaa testikirjastoa. Ohjelmointi alkaa aina testien kirjoittamisella ja jatkuu ohjelmakoodin kirjoittamisella siten, että juuri kirjoitetut testit läpäistään. Testin sisältö siis määrittelee sen, mitä kerralla kirjoitettava ohjelmakoodi tekee. Testin sisällön vastaavasti määritte- lee se, mitä ohjelman tulee tehdä. Tällä tavoin koko ohjelmakoodi tulee automaattisten testien piiriin ja samalla menetelmä ohjaa ohjelmoijaa keskittymään testitapauksilla tar- kennettujen vaatimusten täyttämiseen. (Astels 2003, 7; Koskela 2008, 4-5, 15-19.) Astels (2003, 7) erottaa Test Driven Development -menetelmässä toisistaan kolme eri- laista testaamista: ohjelmoijan testit, yksikkötestit sekä asiakastestit. Yksikkötestit tes- taavat, että kirjoitettu koodi toimii teknisesti eli ohjelmakoodi voidaan kääntää tai muu- ten suorittaa kokonaan ilman virheitä. Asiakastestit testaavat sen, että ohjelmisto tekee asiakkaan tai ohjelman käyttäjän näkökulmasta sen, mitä sen pitäisikin tehdä. Ohjelmoi- jan testit sen sijaan testaavat, että teknisesti toimiva koodi tekee sen, mitä sen toimin- nallisesti halutaan tekevän määritellyissä rajoissa. Käytännössä se tarkoittaa, että esi-

(17)

merkiksi maksutapahtumaan ei saa kyetä syöttämään negatiivista summaa tai uuden salasanan tulee olla erilainen kuin mitä edellinen salasana oli.

Sekä Astelsin (2003, 6-8) että Koskelan (2008, 19-22) mukaan testit edellä -

menetelmissä ohjelmointi tehdään erittäin pienissä sykleissä. Testit suunnitellaan silloin, kun ohjelmakoodin toiminnallisuus eli sekä vaatimukset että lopullinen toteutus ovat vielä ohjelmoijalla hyvin muistissa. Astelsin mukaan jokainen ohjelmointisykli alkaa testin ohjelmoinnilla ja sen onnistuneella, mutta virhetuloksen tuottaneella testiajolla, jatkuu testit läpäisevän ohjelmakoodin kirjoittamisella sekä kirjoitetun koodin refakto- roinnilla päättyen virheettömiin testituloksiin. Tämän Astels perustelee William Waken (2001) esittämällä liikennevalo-mallilla:

1. Aloita. (Vihreä valo!) 2. Ohjelmoi testi.

3. Suorita testiajo.

Ohjelmakoodin kääntäminen epäonnistuu, koska kutsuttuja metodeita ei ole vielä ohjelmoitu. (Keltainen valo!)

4. Lisää puuttuva, mutta tyhjä metodi.

5. Suorita testiajo.

Testiajo epäonnistuu, koska tyhjä metodi ei palauta vielä mitään. (Punainen valo!) 6. Ohjelmoi testi palauttamaan jotakin.

7. Suorita testiajo.

Testi onnistuu. (Vihreä valo!) 8. Aloita uusi ohjelmointisykli.

Ohjelmointisykli alkaa mahdollisimman yksinkertaisen testin kirjoittamisella. Mahdolli- simman yksikertainen testi tarkoittaa, että ennen ensimmäisenkään luokan tai metodin toiminnallisuuden ohjelmoimista suoritetaan vain tulevan luokan olemassaolon testaava testitapaus. Keltainen on merkkinä siitä, että ensimmäisessä testiajossa metodeita ei ole vielä edes olemassa ja testitapauksen ajoyritys kaatuu käännösvirheeseen tai vastaavaan.

Seuraavaksi lisätään tarpeelliset metodit ilman toiminnallisuutta. Näin testiajo onnistuu, mutta testi antaa virheellisen tuloksen eli testi on punaisella. Vasta viimeiseksi lisätään metodeihin ohjelman logiikkaa, jolloin testit onnistuvat ja testitulos on vihreä. Logiikan lisääminen aloitetaan ohjelmoimalla metodin halutut palautusarvo sellaisenaan, koska se on askel askeleelta edetessä pienin mahdollinen muutos, jonka voi testata. Vasta en-

(18)

simmäisen testiajon onnistuttua ryhdytään jatkamaan ohjelmointia uudella testitapauk- sella, joka saattaa edellyttää kiinteän metodin palautusarvon korvaamista jollakin ohjel- malogiikalla. (Astels 2003, 8-10; Koskela 2008, 19-22; Wake 2001.)

Koskela kuvaa työnkulkua kuvassa alla olevan kaavion mukaisella Testaa - Ohjelmoi - Suunnittele -mallilla. Koskelan malli kuvaa ohjelmointisyklin työvaiheita, kun Waken liikennevalomalli kiinnittyy testitulosten onnistumiseen. Koskelan mallin viimeisen vai- heen eli refaktoroinnin tai suunnittelun pitäisi pysyä jatkuvasti testituloksiltaan vihreä- nä. Waken ja Koskelan mallit eivät ole ristiriidassa keskenään, vaan tuovat eri näkökul- man samaan prosessiin. (Koskela 2008, 15-17; Koskela 2008, 50-56; Wake 2001.)

Kuva 1 Työnkulku perinteisellä ohjelmistonsuunnittelumallilla (Koskela 2008, 15).

Kuva 2 Työnkulku Test Driven Development menetelmässä (Koskela 2008, 15).

Kuva 3 Ohjelmistokehittäjän hokema Test Driven Development menetelmässä (Kos- kela 2008, 16).

Koskela (2008, 19-22, 47-48) opastaa ohjelmoijaa huolehtimaan testien atomisuudesta ja eristämisestä muista testeistä. Yhdessä testissä saa testata pienintä mahdollista toiminnallisuutta eivätkä testit saa olla riippuvaisia toisistaan. Testit tulee toisin sanoen suunnitella niin, että kukin testi testaa vain yhtä asiaa, testejä tehdään yksi jokaista testattavaa yksityiskohtaa varten ja suoritettavia testejä tai niiden järjestystä pitää voida muuttaa. Erityisenä hyötynä Koskela näkee myös sen, että kun ohjelmointi alkaa testien suunnittelulla ilman todellista ohjelmakoodia, toteutettavasta ohjelmakoodista tulee yksinkertaisempaa. Koska valmista ohjelmakoodia ei ole, se ei estä ohjelmoijaa

Testaa Ohjelmoi Suunnittele

Testaa Ohjelmoi Refaktoroi

Suunnittele Ohjelmoi Testaa

(19)

kuvittelemasta parasta mahdollista tulevaa ohjelmakoodia juuri sillä hetkellä ohjelmoitavan testin näkökulmasta.

Seuraavassa käydään kukin näistä vaiheista tarkemmin läpi. Esimerkkinä olevissa oh- jelmakoodeissa on pyritty tuomaan esille vain kullekin esimerkille olennaisin ohjelma- koodi eivätkä esimerkkinä olevat ohjelmakoodit ole sellaisenaan toimivia. Esimerkeissä kahden eri version välillä muutettu tai muuten merkityksellinen ohjelmakoodi on liha- voitu.

2.1 Vaatimukset testeiksi ja ensimmäinen testiajo

Ohjelmointimenetelmän mallina on esimerkki kuvitteellisen Java-kielisen verkkosovel- luksen käyttäjän mainemittarista, jossa muut käyttäjät voivat antaa arvioita toisten käyt- täjien viestien hyödyllisyydestä itselleen. Malli on muokattu Astelsin (2003, 9-11) kirjas- saan esittämästä esimerkkiohjelmasta. Ensin perustetaan Reputation-luokka ja ohjel- moidaan sille testitapaus:

public void testReputation() {

assertEquals("Bad reputation rating.", 4, reputation.getAverageRating());

}

Seuraavaksi lisätään kohdat, joiden avulla mainearvio saadaan täsmäämään haluttuun arvoon:

public void testReputation() {

Reputation reputation = new Reputation("tester1");

reputation.addRating(3);

reputation.addRating(5); assertEquals("Bad reputation rating.", 4,

reputation.getAverageRating());

}

Kun tämä ohjelmakoodi yritetään ajaa, kääntäjä ilmoittaa virheellisistä metodeista

addRating() ja getAverageRating(). Koska kääntäjä ilmoittaa puuttuvista meto- deista, mutta koodi on muuten oikein, olemme keltaisessa vaiheessa. Tämän jälkeen siir- rytään kirjoittamaan varsinaista ohjelmakoodia.

(20)

2.2 Ohjelmoi testien läpäisemiseksi ja vain testien läpäisemiseksi

Ensimmäisen testin kirjoittamisen jälkeen ohjelmoidaan metodit, kuten sekä Astels (2003, 6-8) että Koskela (2008, 19-22; 2008, 58-63) ovat ohjanneet. Ne toteutetaan al- kuun mahdollisimman yksinkertaisesti eli metodit perustetaan ja ne määritetään palaut- tamaan jonkin kiinteä oletusarvo palautettavan tietotyypin mukaisesti. Kun testit suori- tetaan uudestaan, suoritus onnistuu ja testitulos on virheellinen eli liikennevalo ns.

näyttää punaista. Vasta tämän punaisen valon jälkeen koodia muutetaan niin, että testi- tuloskin on oikein ja testiajo näyttää vihreää. Tätä testien ohjelmointia tehdään niin pitkään, että kaikki erilaiset metodin palauttamat vaihtoehdot saadaan testien piiriin.

Esimerkiksi palautusarvon tyyppi, palautettavan tekstin pituus tai numeromuotoisen palautusarvon ylä- sekä alaraja tulee testata huolellisesti ja tarkistaa metodin hallitusti palauttamat virheet erilaisilla tarkoituksella väärin annetuilla parametreilla, jotta voidaan varmistua metodin selviävän myös virhetilanteista.

Esimerkissä toteutetaan seuraavaksi puuttuvat metodit mahdollisimman yksinkertaises- ti. Aivan ensimmäiseksi annetaan palautusarvoksi kiinteä lukuarvo, koska metodin on tarkoitus palauttaa jokin lukuarvo. Tässä määritellään metodille getAverageRating()

palautusarvoksi 0. Metodi addRating() ei palauta mitään (Astels 2003, 9-10.):

public void addRating(int newRating) { }

public int getAverageRating() { return 0;

}

Koodin kääntäminen ja suoritus onnistuu, mutta itse testi epäonnistuu. Testitulos on siis punaisella. Tämän jälkeen muokataan ohjelmakoodin toiminnallisuutta siten, että myös testitulos on haluttu. Metodia eri varsinaista ohjelmaa muokataan täyttämään tes- tissä testattava toiminnallinen vaatimus. Koska voimme helposti testata tehtyjä muu- toksia, toiminnallisuuksien lisääminen voidaan aloittaa asettamalla ensin

getAverageRating()-metodin palautusarvoksi 4 ja ajaa testit uudestaan. Nyt testitu- loksen pitäisi olla onnistunut eli testitulos on vihreä. Tämän jälkeen voimme muuttaa kiinteän arvon palauttamisen laskutoimitukseksi return (3 + 5) / 2 ja ajaa testit uudestaan. Kun tämäkin onnistuu, voidaan koodia vastaavalla tavalla vähitellen korjata

(21)

ja lisätä keskiarvon laskemiseksi tarpeelliset muuttujat totalPoints sekä

numberOfRatings, joiden avulla metodin toiminnot voidaan toteuttaa:

private int totalPoints = 0;

private int numberOfRatings = 0;

public void addRating(int newRating) { totalPoints += newRating;

numberOfRatings++;

}

public int getAverageRating() {

return totalPoints / numberOfRatings;

}

Tämän jälkeen voidaan tehdä lisää testitapauksia, joilla voidaan esimerkiksi testata an- netun mainepisteytyksen kuuluminen sallittuun asteikkoon syöttämällä esimerkiksi ne- gatiivisia arvoja ja odottamalla, että testi epäonnistuu.

2.3 Koodin refaktorointi

Ohjelmoija voi muuttaa turvallisesti ohjelmakoodia, kun ohjelmakoodin halutut toi- minnot on saatu useiden ohjelmointisyklien jälkeen valmiiksi ja kattavasti testien piiriin.

Nyt ohjelmoijan on yksinkertaista ja nopeaa suorittaa automatisoituja testejä jokaisen pienenkin muutoksen jälkeen. Astels (2003, 11-12) pitää erittäin tärkeänä sitä, että teste- jä suoritetaan jatkuvasti ja niiden käyttäminen ohjelmoinnin tukena on nopeaa ja help- poa. Hänen mukaansa kokemus on osoittanut, että testaamisen ollessa hidasta tai mo- nimutkaista ohjelmoija siirtää testaamista vähitellen eteenpäin ja kasvattaa samalla en- simmäistä kertaa testattavan koodin määrää. Jokin testiajon tuottaessa odottamatta epäonnistuneen testituloksen aiemmin onnistuneen testituloksen sijaan voi ohjelmoijan peruuttaa tai tarkistaa juuri tekemänsä muutoksen. Ohjelmointi- ja testaussykli on olta- va mahdollisimman lyhyt, jotta löytyvät virheet nopeasti eikä niiden etsimiseen kulu tarpeettomasti aikaa.

Astels (2003, 15) korostaa, että ohjelmakoodista ei käytännössä tule koskaan ensimmäi- sellä kirjoittamalla täydellistä, lyhyttä, sisäisesti loogista ja selkeintä mahdollista. Ohjel- makoodia voi aina parantaa, muuttaa lukijalle havainnollisemmaksi ja tehokkaammaksi.

Tämän vuoksi ohjelmakoodia on refaktoroitava, kun se on ensin saatu toimimaan ul- koisesti halutulla tavalla. Refaktorointia voidaan tehdä aina ja käytettävissä olevien re-

(22)

surssien puitteissa jopa loputtomasti. Test Driven Development -menetelmässä kutien- kin on kolme erikseen mainittua tilannetta, jolloin koodia täytyy refaktoroida eli siistiä (Astels 2003, 15.):

− Koodissa on sisäistä päällekkäisyyttä eli jokin asian tehdään miltei samalla tavalla useammassa kohdassa.

− Koodin toiminta ja tarkoitus eivät ole selviä.

− Koodi ns. haisee eli siinä on merkkejä mahdollisista piilevistä ongelmista.

Refaktoroitaessa koodia järjestellään sisäisesti uudella tavalla toimivaksi muuttamatta sen ulkoista toimintaa. Muutetun koodin toimivuudesta ja ulkoisen toiminnan muut- tumattomuudesta voidaan varmistua helposti, jos ohjelmointi on tehty aiemmin kuva- tussa järjestyksessä eli ohjelmoijalla on valmis automatisoitujen testien kirjasto. Refak- toroitaessa koodista poistetaan päällekkäisyys, selkeytetään kunkin ohjelmakoodilohkon tarkoitus sekä poistetaan ns. haiseva ohjelmakoodi eli muista ongelmista kielivät ohjel- moinnin rakenteet. (Astels 2003, 15; Koskela 2008, 24-27.)

Astelsin mukaan (2003, 17) refaktoroitaessa on keskeistä suhtautua koodiin toisin kuin ohjelmoitaessa. Ohjelmoijan ei tule miettiä kirjoitusvaiheessa koodin refaktoroimista lainkaan vaan keskittyä ohjelman toiminnallisuuteen. Vastaavasti refaktoroitaessa keski- tytään vain siihen eikä ohjelmaan lisätä lainkaan uutta toiminnallisuutta. Tämän vuoksi ohjelmoijan on keskityttävä vain jompaankumpaan tehtävään kerrallaan. Niin pitkään, kun testin ajaminen tuottaa virheen, ollaan ohjelmointivaiheessa. Kun testiajo onnistuu, on refaktoroinnin vuoro eikä koodiin lisätä enää toiminnallisuuksia. Seuraavaksi tarkas- tellaan lähemmin refaktorointia.

2.3.1 Päällekkäisyyden poistaminen

Koodin päällekkäisyys eli saman toiminnallisuuden toistaminen useammassa eri paikas- sa on sekä Astelsin (2003, 16) että Koskelan (2008, 8) mukaan erittäin haitallista ja siitä tulee ehdottomasti hankkiutua eroon. Koskela (2008, 27) esittää päällekkäisyyden pois- tamisen olevan jopa tärkein yksittäinen refaktoroinnin syy. Päällekkäistä koodia tulee tarkastella huolellisesti ja siirtää tarpeellinen toiminnallisuus yhteen paikkaan. Jos esi- merkiksi kaksi metodia sisältää lähes identtisen toiminnon, voidaan tämä toiminto joko erottaa kokonaan omaksi metodikseen tai jättää vain se toiseen metodiin. Esimerkkinä

(23)

päällekkäisyyttä sisältävät kaksi metodia, jotka tallentavat ensimmäisen arvion ensim- mäistä kertaa tai päivittävät vanhan arvon uudella keskiarvolla ja arvioiden määrällä (Astels 2003, 17):

public boolean saveRating() throws CustomException { DatabaseConnection targetDb = new

MySQLConnection();

rating.saveTo(targetDb);

targetDb.close();

return true;

}

public boolean updateRating() throws CustomException { addRating(Rate);

DatabaseConnection targetDb = new MySQLConnection();

rating.saveTo(targetDb);

targetDb.close();

return true;

}

Esimerkkimetodit ovat sisäisesti lähes identtiset, vain updateRating() -metodin en- simmäinen rivi eroaa saveRating() -metodista. Koska metodeissa on lähes identti- nen sisältö, voidaan updateRating() -metodin toiminnallisuus poistaa lähes koko- naan ja käyttää saveRating() -metodia tiedon tallentamiseen (Astels 2003, 17):

public boolean updateRating() throws ConnException { addRating(Rate);

saveRating();

}

Päällekkäisen koodin poistaminen johtaa helposti myös koodin käytettävyyden para- nemiseen, kun koodia muunnetaan vähemmän yksityiskohtaiseksi ja siten laajemmalti käytettävissä olevaksi. Toinen tapa poistaa päällekkäisyyttä on poistaa koodista tietoa tai muuttujia, jotka ovat kahdentavat tiedon käsittelyä. Tarpeetonta tietoa voidaan kar- sia esimerkiksi poistamalla metodista taulukon elementtien erillinen laskuri (Astels 2003, 17):

(24)

public class customerToCustomerRatings() { private int numberOfRatings = 0;

private Collection ratings = new ArrayList();

public int size() {

return numberOfRatings;

}

public void add(Rating newRating) { ratings.add(newRating);

numberOfRatings++;

} }

Laskuri on tarpeeton, koska kaikki arviot ovat tallella ratings-taulukossa ja arvioiden määrä on sama kuin sen elementtien lukumäärä (Astels 2003, 18):

public class customerToCustomerRatings() {

private Collection ratings = new ArrayList();

public int size() {

return ratings.size();

}

public void add(Rating newRating) { ratings.add(newRating);

} }

2.3.2 Koodin tarkoituksen selkiyttäminen

Koodin on ohjelmoijan tärkein suorite. Sen vuoksi koodin pitää olla merkityksellinen ja sen tarkoituksen tulee olla täysin selvä. Askel (2003, 18) kehottaa huolehtimaan viimeis- tään refaktoroitaessa siitä, että koodi selittää itse itsensä. Hyvä ja helppo ohje on antaa kaikille ohjelmakoodin osille kuten metodeille ja muuttujille semanttisesti merkitykselli- set nimet (Astels 2003, 45-48). Astelsin mukaan ohjelman osan nimen pitäisi kuvata mitä esimerkiksi metodi tekee tai muuttuja sisältää. Myös Koskela (2003, 25) korostaa

(25)

nimeämiskäytäntöä koodien selkeyden parantamisessa ja huomauttaa, että yleisin refak- torointitapahtuma on metodin uudelleennimeäminen.

Luokan nimeen kannattaa lisätä substantiivi, joka kuvaa mitä luokka edustaa tai tekee (Astels 2003, 45):

public class Rating {...}

public class RatingComparator implements Comparator {...}

public class XMLRatingExport implements RatingExport {...}

Metodin nimeen kannattaa vastaavalla tavalla liittää verbi, joka kuvaa metodin toimin- nallisuutta (Astels 2003, 46):

private int calculateAverageOfGivenRatings() {...}

Nimeämisessä kannatta käyttää yleisiä nimeämisnormeja. Kussakin ohjelmointikielessä sekä usein myös yrityksen tai jopa sovelluksen viitekehyksessä on omia nimeämisnor- mejaan. Javassa ja monessa muussakin ohjelmointikielessä luokkiin on suositeltavaa tehdä getX() ja setX() -metodit. Totuusarvon palauttavat metodit nimetään usein

isX()- tai hasX() -muotoon (Astels 2003, 47):

public User getUser(int UID) {...}

public void setUserID(int ID) {...}public boolean isLogged() {...}

public boolean hasUserName() {...}

Astels (2003, 48) tuo esiin myös sen, että tarpeettoman tiedon lisääminen metodien nimiin voi aiheuttaa ongelmia myöhemmin. Esimerkiksi addClassX(ClassX parameter) -metodin parametrina olevan luokka ClassX nimeä muutettaessa jää

addClassX()-metodi helposti nimeämättä ja muutoksen jälkeen metodin nimi ei ole enää yhteneväinen lisättävän luokan kanssa. Tämä aiheuttaa herkästi väärinkäsityksiä myöhemmin. Metodin ylikuormituksen1 sallivissa ohjelmointikielissä onkin tarpeetonta lisätä parametrina annettavan luokan nimeä metodin nimeen, koska add() -metodeita voidaan toteuttaa yksi jokaista tarpeellista lisättävää luokkaa kohden:

1 Eng. overloading

(26)

public void add(Rating rating) {...}

public void add(RatingComment ratingComment) {...}

2.3.3 ”Haisevan koodin” siivoaminen

Haisevalla koodilla tarkoitetaan Astelin (2003, 18) mukaan Test Driven Development - menetelmälle läheistä Extreme Programming -menetelmää käyttävien ohjelmoijien kes- kuudessa huonolaatuista ohjelmakoodia. Haisevassa koodissa on tarpeettomasti kom- mentteja, liian läheisiä luokkien välisiä suhteita, tietoluokkia, suuriksi paisuneita tai liian pieniä luokkia, pitkiä metodeita, huonosti suunniteltuja switch-rakenteita tai sellainen ohjelmakoodin rakenne, jossa tehtävät muutokset johtavat Astelsin (2003, 25) haulik- kokirurgiaksi nimittämään1 toimintaan. Tällä hän tarkoittaa, että ohjelman jonkin me- todin tai luokan muuttaminen johtaa muutoksiin useassa muussa kohdassa ohjelmaa tai esimerkiksi jonkin muun luokan sisältävän switch-lauseen muuttamiseen. Astels näkee tällaisessa rakenteessa odotettavissa olevia ongelmia, kun jollakin kertaa ohjelmaa muu- tettaessa jokin muutoksesta riippuvainen kohta jää korjaamatta ja johtaa ohjelman vir- heelliseen toimintaan.

Astels (2003, 53) esittää, että ohjelmakoodin sisään kirjoitetut kommentit ovat usein tarpeettomia ja osoittavat ohjelmakoodista helposti sellaiset paikat, jossa koodi on epä- selvää, huonosti kirjoitettua, käytetty nimeäminen ei selitä koodin toimintaa, logiikka on ontuvaa jne. Näihin kotiin on lisätty kommentteja, jotta koodin lukeminen ja ymmär- täminen olisi helpompaa. Jos kommentti selittää, mitä ohjelmakoodi tekee, on ohjelma- koodia todennäköisesti refaktoroitava selkeämmäksi. Ohjelmakoodin tulee aina olla riittävän selvää, jotta sen toiminnan tarkoitusta ei tarvitse selittää kommentteja kirjoit- tamalla. Trucchi ja Romei (2010, 31-33) esittävät, että monesti kommentoitu kohta voidaan muuntaa omaksi metodikseen. Kommenttia voidaan hyödyntää metodin ni- meämisessä, koska näin menetellessä metodin nimestä tulee kuin itsekseen metodin tarkoituksen selittävä ja siten helposti ymmärrettävä. On kuitenkin myös muistattava, että kommentteja voidaan oikein hyvin käyttää selittämään miksi ohjelmassa tehdään jotakin, vaikka on tarpeetonta selittää miten se tapahtuu. Usein nämä kommentit lisäävät ohjelmakoodin luettavuutta.

1 Eng. shotgun surgery

(27)

Astelsin (2003, 54-55) mukaan hyväksyttäviä syitä ohjelmakoodin kommentoimiseen on:

− epätäydellinen ohjelmakoodi, esimerkiksi //TODO -merkintä kohdassa, johon on syytä vielä palata,

− refaktorointia tarvitseva ohjelmakoodin kohta, esimerkiksi //NEEDS WORK - merkinnällä alkava muistiinpano,

− algoritmin käyttäminen epätavallisella tavalla, jolloin sen käyttöä voidaan lyhyesti perustella kommentilla,

− muualla julkistetun algoritmin tai ohjelmakoodin käytön yhteydessä lähdeviittaus ja alkuperäisen tekijän mainitseminen,

− suorituskyvyn parantamiseen liittyvät kohdat, jotta muut ohjelmoijat eivät koodia refaktoroidessaan aiheuta epähuomiossa suorituskyvyn heikkenemistä sekä

− luokan alussa oleva kommentti, jossa poikkeuksellisesti saadaan lyhyesti selittää miksi luokka on toteutettu ja mihin sitä käytetään.

2.3.4 Muut ohjelmakoodin ongelmat

Joskus ohjelman luokista tulee Astelsin (2003, 21-22) mukaan liian läheisiä. Sillä hän tarkoittaa luokkia, jossa jokin luokkaa käyttää huomattavaa osaa toisen luokan muuttu- jista tai metodeista. Rakenteellisesti on parempi pitää luokan tietoihin kiinteästi liittyvät toiminnallisuuden luokan sisällä eikä viitata niihin toisesta luokasta, koska alkuperäisen luokan rakenteen muuttaminen johtaa toisen luokan muuttamiseen. Esimerkiksi alla olevassa AllRatings-luokassa saveTo() -metodi käyttää huomattavasti Rating - luokan metodeja hyväkseen:

public class AllRatings { //...

(28)

public void saveTo(MySQLConnect dbConn) throws SQLException { Iterator ratingIterator = ratings.iterator();

while (ratingIterator.hasNext()) {

Rating rating = (Rating)ratingInterator.next();

String sql = "INSERT INTO rating ";

sql += "(uid, rating, role) VALUES (" ;

sql += "'" + rating.getUID().toString() + "', ";

sql += "'" + rating.getRating().toString() + "', ";

sql += "'" + rating.getRole().toString() + "', ";

try {

dbConn.executeQuery(query);

} catch (SQLException e) { e.printStackTrace();

} }

} }

Refaktoroitaessa voidaan sql-kyselyn muodostaminen siirtää suoraan Rating-luokkaan, jolloin AllRatings-luokan rakenne selkiytyy. Samalla tiiviisti Rating-luokkaan ja sen rakenteeseen liittyvä tietokantakyselyn rakentaminen vastuutetaan oikealle luokalle:

public class AllRatings { //...

public void saveTo(MySQLConnect dbConn) throws SQLException {

Iterator ratingIterator = ratings.iterator();

while (ratingIterator.hasNext()) { Rating rating =

Rating)ratingInterator.next();

rating.saveToDB(dbConn);

} }

}

public class Rating { //...

public void saveToDB (MySQLconnect dbConn)) { String sql = "INSERT INTO rating ";

(29)

Sql += "(uid, rating, role) VALUES (" ; sql += "'" + getUID().toString() + "', ";

sql += "'" + getRating().toString() + "', ";

sql += "'" + getRole().toString() + "', ";

try {

dbConn.executeQuery(query);

} catch (SQLException e) { e.printStackTrace();

} }

}

Tietoluokat ovat Astelsin (2003, 19) mukaan poistettava. Tietoluokalla hän tarkoittaa luokkaa, joka sisältää enimmäkseen tietoa eikä tarjoa juurikaan toimintoja eli metodeja.

Trucchia ja Romei (2010, 21) tarkentavat määritelmää niin, että tietoluokassa attribuutit ovat usein julkisia1 ja luokka ei sisällä liiketoimintalogiikkaa eli ohjelmalle merkitykselli- siä toimintoja getX()- ja setX() -metodeita lukuun ottamatta. Astels (2003, 19) pai- nottaa, että erityisesti julkiset muuttujat osoittavat luokan tarvitsevan refaktorointia.

Ohjelmassa saattaa olla muita luokkia, jotka tarvitsevat tietoluokan muuttujia omissa metodeissaan. Refaktoroitaessa voidaan tietoluokka yhdistää johonkin ulkopuoliseen luokkaan tai siirtää muiden luokkien toiminnallisuutta eli metodeja tietoluokkaan.

Luokkien olisi Astelsin (2003, 31-33) mukaan hyvä olla kooltaan suunnilleen samanko- koisia. Selvästi muita luokkia isommaksi kasvanut luokka kannattaa jakaa pienempiin osiin. Luokkien kokoa voi suhteuttaa esimerkiksi vertailemalla koodirivien määrää tai tarkastelemalla luokkia UML-kaavioina. Refaktoroitaessa isoja luokkia pienemmiksi voidaan tarkastella esimerkiksi sitä, onko luokassa liikaa toimintoja, onko se jaettavissa pienempiin ja helpommin hallittavissa oleviin luokkiin, tai voidaanko luokkaa pienentää purkamalla sitä useampaan luokkaan polymorfismia hyödyntämällä. Myös sellaiset luo- kat, joissa on muuttujalla määritetty olion tyyppi, voidaan olio-ohjelmoinnin periaattei- den mukaisesti purkaa yliluokaksi ja siitä periytetyiksi aliluokiksi.

public class VegetarianProduct {

// 0=fruit, 1=root_vegetable, 2=other_veggie

1 public

(30)

private int prodType;

//...

public String getProductType() { switch (int prodType) {

case 0:

return "fruit";

case 1:

return "root_vegetable";

case 2:

return "other_veggie";

default:

return "Other";

} }

}

Purkamalla kasvisten tuotetyypit omiksi VegetarianProduct -luokan alaluokiksi koodi selkenee ja samalla päästään tarpeettomasta switch-rakenteesta eroon (Astels 2003, 31-33):

abstract public class VegetarianProduct { public abstract String getProductType();

}

public class Fruit extends VegetarianProduct () { public String getProductType() {

return "Fruit";

} }

public class RootVegetable extends VegetarianProduct () { public String getProductType() {

return "root_vegetable";

} }

public class OtherVeggie extends VegetarianProduct () { public String getProductType() {

return "other_veggie";

} }

(31)

Pienten luokkien kohdalla kannattaa miettiä, voisiko luokan yhdistää johonkin toiseen luokkaan samaan tapaan, mitä lähinnä tiedon säilyttämiseen käytetylle tietoluokalle teh- tiin. Pitkät metodit ovat suureksi kasvaneiden luokkien tapaan ongelma. Pitkän meto- din ymmärtäminen myöhemmin on vaikeaa ja refaktorointivaiheessa metodista tulisi pyrkiä erottamaan loogiset kokonaisuudet pienemmiksi metodeiksi, joiden hallinta ja käsittely on myöhemmin helpompaa. Astelsin (2003, 24) mukaan metodin ylittäessä 10 ohjelmointiriviä se on jo liian pitkä. Toinen tarkasteltava seikka on metodin suorittami- en tehtävien määrä. Jos metodi tekee enemmän kuin yhden asian, se tulee jakaa use- ammaksi yhden tehtävän sisältäväksi metodiksi.

Proseduraalinen ohjelmakoodi tulee purkaa refaktorointivaiheessa. Proseduraalisella koodilla tarkoitetaan sellaista ohjelmakoodia, jossa ei hyödynnetä suunnittelu- tai oh- jelmointimalleja eikä käytetä tehokkaasti olio-ohjelmoinnin hyödyllisiä ominaisuuksia.

Proseduraalinen ohjelmakoodi sisältää Trucchian ja Romein mukaan usein paljon pääl- lekkäistä ohjelmakoodia, joka vaikeuttaa merkittävästi ohjelmiston ylläpitoa ja heikentää sen muunneltavuutta. (Trucchia & Romei 2010, 24.)

2.3.5 Refaktoroinnin periaatteita

Refaktorointia voidaan edellä esitetyn toimivan, siistin ja helposti omaksuttavissa ole- van koodin periaatteiden mukaan ajatella sarjana tarkistettavia ja mahdollisesti puretta- via toimenpiteitä. Astels (2003, 26-42) kertoo omat kymmenen tärkeintä refaktoroinnin ohjenuoraansa:

− Jaa isot luokat pienemmiksi luokiksi, selvennä luokkien toimintoja erottamalla ne pieniksi ja helposti omaksuttaviksi luokiksi.

− Erota rajapinta1 luokasta. Osa ohjelmakoodista voidaan siirtää rajapintaan ja jolloin luokan ohjelmakoodin määrä pienenee. Rajapinnan erottamisesta on etua myös tes- tauksen yhteydessä, kun halutaan käyttää mock-olioita.

− Erota metodeista loogiset kokonaisuudet omiksi metodeikseen ja käytä uusia me- todeita, kun tarvitset erotettuja toiminnallisuuksia. Tämä parantaa koodin luetta- vuutta erityisesti silloin, kun nimikäytäntö on selkeä ja havainnollinen.

1 interface

(32)

− Poista luokan tyypin määrittelevät merkinnät ja ehdolliset rakenteet, kuten switch- lohkot.

− Vähennä toistoa siirtämällä samanlaiset tai lähes samanlaiset aliluokkien metodit yliluokkaan. Pienten luokkien välisten erojen toteuttamiseksi voidaan toteuttaa esimerkiksi uusi apumetodi yliluokkaan ja siten korvata parilla yliluokan metodilla kaikki aliluokkien toisiaan muistuttavat metodit.

− Pura monimutkaiset sisäkkäiset rakenteet ja laskutoimitukset pienempiin osiin.

Käytä tarvittaessa metodin sisäisiä ja väliaikaisia, havainnollisesti nimettyjä muuttu- jia välitulosten tallentamiseen.

− Korvaa luokan konstruktori Factory Methodilla. Java- ja C++-ohjelmointikielissä voidaan luokkien konstruktoreita ylikuormittaa, mutta Factory Methodilla kon- struktoreita voidaan toteuttaa kaikissa olio-ohjelmointikielissä jokaista erilaista luo- kan perustamistapausta varten itse, nimetä havainnollisemmin ja ketjuttaa näiden kautta suojattuun1 luokan konstruktoriin oletusarvojen kanssa.

− Korvaa koodin sisältämät kiinteät lukuarvot vakioilla. Koodin sisältämät lukuarvot ja merkkijonot ovat hankalia havaita. Niiden muutostarve aiheuttaa helposti on- gelmia, kun jokin kohta jää muuttamatta. Lukuarvot voidaan korvata isoin kirjai- min kirjoitetulla vakiolla, jonka arvo määritellään luokan alussa.

− Korvaa sisäkkäiset ehtolauseet vahtilauseilla2 luettavuuden parantamiseksi. Esimer- kiksi pitkä, mutta yksinkertaisia testejä ja palautusarvoja sisältävä if-elseif- elseif-then -ehtolauseke voidaan hyvin korvata muutamalla yksinkertaisemmal- la if-lauseella. Kun nämä vielä muotoillaan yhden rivin lauseiksi, paranee koodin luettavuus selvästi sisäkkäisiin ehtolauseisiin verrattuna:

public int test(int i) { if (i == 0) return 1;

if (i == 1) return 1;

return i*i;

}

1 private

2 guard clauses

(33)

Astels (2003, 42) esittää refaktoroinnin tehokkuuden ja ohjelmoijan taidon perustuvan laajaan erilaisten suunnittelumallien1 tuntemiseen. Mitä enemmän ohjelmoija tuntee erilaisia suunnittelumalleja, sen helpommin niitä myös tunnistaa ja pystyy soveltamaan käytännön ohjelmointityössä. Suunnittelumalleja ei kuitenkaan hänen mukaansa pidä käyttää ainoana lähestymistapana, vaan käyttää niitä refaktoroinnin työkaluna ja vain tarpeen mukaan.

2.4 Tuottavuuden muutos

Tutkimusten mukaan Test Driven Development -menetelmää noudattaessa sekä tuo- tettavan ohjelmiston laatu että varsinaisen tuotannon tehokkuus paranevat (Sims 2009;

Trucchia & Romei 2010, 50-51). Trucchia ja Romei (2010, 50-51) kuvaavat perinteisen ja refaktorointia hyödyntävän ohjelmointimenetelmän välistä pitkän aikavälin kustan- nuseroa sillä, että perinteisen menetelmän yhteydessä ohjelmiston käyttöönoton jälkeen tapahtuva ohjelmiston virheiden korjaus ja muutosten teon vaikeutuminen lisäävät kus- tannuksia nopeasti. Refaktoroidun sovelluksen kustannukset pysyvät maltillisina, koska ohjelmiston rakenne on helpompi selvittää, se pystytään testaamaan ja mahdolliset vir- heet paikantamaan nopeasti ja tehokkaasti.

Kuva 4 Perinteisen ja refaktorointia hyödyntävän ohjelmistokehityksen pitkän aikavälin kustannusrakenteen erot (Trucchia & Romei 2010, 50-51).

Test Driven Development on sekä joukko hyviä ohjelmointitapoja että uudenlainen tapa ajatella ohjelmointiprosessia. Tehokkaasti hyödynnettynä se vie ohjelmoinnin aluk- si hieman perinteistä ohjelmistokehitystä enemmän aikaa, koska ohjelmoitaessa kirjoi-

1 Eng. design patterns

(34)

tettava ohjelma sisältää testit ja ohjelmakoodin määrä on sen vuoksi suurempi. Testien avulla voidaan varmistus ohjelman ja sen eri osien toimivuudesta. Test Driven Deve- lopment menetelmää voidaan soveltaa myös muissa kuin Java-kielisissä ohjelmointipro- jekteissa. Seuraavassa kappaleessa tutustutaan PHP-kieleen sovelletun PHPUnit-

testauskehyksen käyttöön, jonka avulla myös PHP-kielisten ohjelmistojen kehittämises- sä voidaan noudattaa testit edellä -periaatetta.

(35)

3 PHPUnit-ohjelmointikehys

PHP on avoimen lähdekoodin ohjelmointikieli, joka on vapaasti kaikkien halukkaiden käytettävissä. Se on laajasti käytetty ohjelmointikieli verkkosivujen toteutuksessa. PHP muistuttaa rakenteeltaan C, Java ja Perl -ohjelmointikieliä ja on suunniteltu kieleksi, jolla voidaan nopeasti toteuttaa dynaamisia verkkosivuja. PHP-koodia voidaan upottaa suoraan HTML-sivun sisään, jossa se suoritetaan ajonaikaisesti. Alla on yksinkertainen esimerkki HTML-sivun sisällä olevasta PHP-lohkosta, joka tulostaa sivunlatauksen yh- teydessä aikaleiman osaksi sivun HTML-koodia. PHP-kielellä voidaan toteuttaa paljon muutakin kuin dynaamisia lohkoja osana HTML-sivuja. PHP-kieli on dokumentoitu kattavasti php.net -sivustolla. Sivustolta löytyy sekä normaalin PHP-asennuksen muka- na asentuvien funktioiden että huomattava määrä PHP:lle ohjelmoitujen laajennusten funktioiden dokumentaatiota. (Php.net 2011b; Php.net 2011c.)

[index.php]

<!DOCTYPE html>

<html lang="en">

<meta charset=utf-8 />

<head>

<title>A PHP-example</title>

</head>

<body>

<h1>We have a timestamp!</h1>

<p>Print out current server timestamp below here:<br />

<div class="time"><?php print date("j.n.Y G:i:s");

?></div>.</p>

</body>

</html>

Sebastian Bergmann (2005-2011a) on toteuttanut testauskehyksen PHP:lle Java-kielisen JUnit-testauskehyksen pohjalta. PHPUnit-ohjelmointikehyksellä voidaan ohjelmoida PHP-sovellukselle Test Driven Development -menetelmän edellyttämät testitapaukset samalla tavoin kuin JUnitilla Java-kieliselle ohjelmistolle. Testien ohjelmoiminen on nopeaa ja testien ajaminen helppoa, joten ohjelmoijan tulee käytetyksi niitä riittävän usein. Ohjelmointivirheet löytyvät nopeasti ja tarkasti, koska PHPUnitilla voidaan to-

(36)

teuttaa riittävän tarkkoja testejä. PHPUnitilla voidaan (Trucchia & Romei 2010, 65;

Bergmann 2005-2011a; van Dam 2009):

− testata tietokanta,

− käyttää mock-olioita, kun halutaan testata luokkien muista luokista riippuvaa toi- minnallisuutta,

− järjestellä testejä sarjoihin ja ryhmiin1,

− valita suoritettavat testit isommasta testijoukosta,

− suorittaa haluttuja toimintoja ennen testejä tai niiden jälkeen,

− kirjata tapahtumia lokiin erilaisissa muodoissa (XML, JSON, TAP, GraphViz, jne.),

− luoda toiminnallisia testejä Selenium RC:lle,

− integroida testejä muihin ohjelmistoihin, kuten Apache Maven, Bamboo, Bitten, CruiseControl, Parabuild, jne.

PHPUnit-ohjelmointikehys antaa ohjelmoijalle mahdollisuuden tehdä sekä yksikkötes- tejä että toiminnallisia testejä. Yksikkötestit on tarkoitettu testaamaan pienimpiä testat- tavissa olevia ohjelmiston kokonaisuuksia ja niillä testataan pääasiassa luokkien ja me- todien toimintoja. Toiminnalliset testit testaavat, että ohjelmisto toimii kokonaisuutena oikein. Esimerkiksi käyttöliittymän toimivuus testataan toiminnallisuustesteillä. Ensin ohjelmoija testaa ohjelman sisäisen toiminnan eheyden, jonka jälkeen toiminnallisissa testeissä mahdollisesti löydetyt virheet tai muutostarpeet on turvallisempaa toteuttaa yksikkötestien tukemana. (Trucchia & Romei 2010, 58.)

PHPUnit-ohjelmointikehyksen kehittäjän Sebastian Bergmannin (2005-2011b) verkos- sa ylläpitämä käyttöohje sisältää asennusohjeet ja ohjeet PHPUnitille tehtyjen laajennus- ten asentamiseksi. Trucchia ja Romei (2010, 65-66) ohjeistavat kirjassa Pro PHP Refak- toring asennuksen huolellisemmin ja ovat sisällyttäneet ohjeisiin myös PHPUnitin ma- nuaalisen asennuksen, joka puuttuu Bergmannin ohjeista. Tässä opinnäytetyössä ei kä- sitellä ohjelmointikehyksen asentamista, koska se on dokumentoitu kattavasti näissä lähteissä.

1 Eng. suites, groups

(37)

3.1 Yksikkötesti

Yksikkötesti on jonkin tietyn ohjelmiston osan toimivuuden tarkistava testi. Yksikkö- testeillä testataan pienimpiä mahdollisia osia eli luokkien ja niiden metodien toimintaa.

Jos esimerkiksi metodin on tarkoitus laskea laskulle eräpäivä, voidaan yksikkötesteillä tarkistaa eräpäivän laskeminen eri tilanteissa ja erilaisilla syöttöarvoilla. Kaikki PHPU- nit-yksikkötestit periytetään PHPUnit_Framework_TestCase -luokasta, joka sisältää testauksessa käytetyt assertXxx() -tyyppisesti nimetyt metodit. PHPUnitilla testates- sa luokat testataan siten, että mahdolliset riippuvuudet muihin luokkiin eliminoidaan.

Nämä riippuvuudet voidaan tarvittaessa simuloida, jotta testi voidaan kohdistaa halut- tuun koodin osaan ja eristää se mahdollisista muun ohjelmiston virheistä. Testien kir- joittamisessa on noudatettava määriteltyä nimeämiskäytäntöä tai dokumentointitapaa, jotta PHPUnit löytää ja suorittaa halutut testit. Metodien nimien pitää alkaa test - sanalla, esimerkiksi testDueDate(), tai vaihtoehtoisesti metodin dokumentaatioloh- kossa tulee olla @test-merkintä. Eräpäivän laskemiseen käytetyn DueDate -luokan ensimmäinen testi voisi esimerkiksi tarkistaa, että luokan palauttama eräpäivä on sama, kuin mitä sille syötetään (Trucchia & Romei 2010, 58; Trucchia & Romei 2010, 66-70):

class DueDateTest extends PHPUnit_Framework_TestCase { public function testGetDueDate() {

$dueDate = new DueDate();

$dueDate->dueDate = "24.12.2011";

$this->assertEquals("24.12.2011",

$dueDate->getDueDate());

} }

class AnotherDueDateTest extends PHPUnit_Framework_TestCase { /**

* alternative to prefixing method names is to use * "@test" in methods dockblock [ie. here]:

* @test */

public function notFollowedNamingConvetionGetDueDate() { //...

} }

(38)

3.2 Toiminnallinen testi

Yksikkötestien lisäksi ohjelmisto täytyy testata myös loppukäyttäjän näkökulman kan- nalta. Tämä tehdään ohjelmoimalla toiminnallisia testejä. Toiminnallisissa testeissä ei käytetä enää simuloituja mock-olioita vaan testin kohteena on koko sovellus käyttöliit- tymän, controllerin, toiminnallisen logiikan ja usein myös aitojen tietojen kanssa. Toi- minnallisille testeille ei ole vielä xUnit-kehitysympäristön tapaan pitkälti standardiksi noussutta kehystä, mutta Selenius RC on laajasti käytetty toiminnallisten testien ohjel- mointirajapinta (API). Selenium RC -testit periytetään

PHPUnit_Extensions_SeleniumTestCase -luokasta. Selenium RC -luokalle voi- daan määritellä testissä käytettäväksi tietty selain sekä simuloida käyttäjän toimintaa, kuten tietyn painikkeen painamista ja siitä seurannutta toimintaa (Trucchia & Romei 2010, 58-59; Trucchia & Romei 2010, 77-84; Bergmann 2005-2011i):

class Example extends PHPUnit_Extensions_SeleniumTestCase { function setUp() {

$this->setBrowser("*firefox");

$this->setBrowserUrl("http://www.google.com/");

}

function testMyTestCase() {

$this->open("/");

$this->type("q", "selenium rc");

$this->click("btnG");

$this->waitForPageToLoad("30000");

$this->assertTrue($this->isTextPresent("Results * for selenium rc"));

} }

Toiminnallisia testejä voidaan toteuttaa myös Firefox-selaimeen saatavana olevalla Se- lenium IDE -lisäosalla ja siihen saatavana olevilla laajennuksilla (SeleniumHQ). Tällä laajennuksella voi kehittäjä nauhoittaa toimintosarjoja ja varmistua eri tilanteissa verk- kosivun eri elementtien sisällöstä. Laajennus voi nopeuttaa kehitystyötä, koska pitkien toimintosarjojen nauhoittaminen ja suorittaminen on helppoa ja Selenium -testejä voi muokata lisäosassa tarpeen mukaan. PHPUnitin Selenium-testeillä voidaan kuitenkin toteuttaa kattavat ja erilaiset virhetilanteet kattavat testit oletettavasti nopeammin kuin

Viittaukset

LIITTYVÄT TIEDOSTOT

Tämän takia muutosten jälkeen pitäisi aina suorittaa regressiotestaus, jotta voidaan varmistua siitä, että ohjelmisto ei mene miltään osin rikki...

Automaatiotestit testaavat usein toiminnallisuuksia siten, että yksittäi- sen testin suorittaminen vaikuttaisi myös toisen testin suorittamiseen esimerkiksi siten, että testi

Edellisessä luvussa käsitellyt heuristiset testit eivät anna täyttä varmuutta tarkasteltavan luvun jaollisuudesta. Fermat'n testin ja Fibonaccin testin yhteydessä havaittiin,

Tämä ei siis tarkoita sitä, että asiat olisivat juuri nyt huonosti tai että ny- kyiset toimintamallit eivät toimisi.. Sitä vastoin se tarkoittaa sitä, että aina on

Teoksen artikkelien pohjalta voidaan siis ajatella että aktivointipolitiikka jatkuu Suomessa myös tulevaisuudessa, vaikka sen... n

Ihan sama mitä se on; se vain ilmes - tyy aina juuri silloin, kun alkaa miettiä, että jotain makeeta tekis mieli.. Lempipartikkeli:

Luku alkaa alkaa niin sanottua teoreettista tutki- musta käsittelevällä artikkelilla (Weckroth) ja jatkuu havainnoilla siitä, miten teorias- ta puhutaan (Suoranta ja J.

- virtalukon virta (Ignition): Mittaa, että jännite on 12V tai 24V silloin, kun ajoneuvon virta on päällä Jos testit läpäistään, kaapelit on kytketty oikein.. Näppäimistön