Blogi

WordPress ja testivetoinen kehitys (TDD)

tl;dr

Kehittämö on ottanut hiljalleen käyttöön TDD:n tapaisen toimintamallin WordPress-kehityksessä. Käyttöönotto on vielä kesken, eikä se ole sujunut täysin ongelmitta. Taustalla on WP-palvelun tarjoama kehitysympäristö, jonka mukana tulee RSpec-frameworkin ympärille tehdyt oletustestit. Toimivat testit voivat pelastaa päivän (tai viikonlopun)!

Mistä TDD:ssä on kyse?

  1. Kirjoita testi
  2. Aja testit (uusi testi ei saa rikkoa vanhoja)
  3. Kirjoita koodia, joka suorittaa halutun toimenpiteen ja läpäisee luodun testin
  4. Aja testit
  5. Refaktoroi koodia
  6. Toista aikaisemmat vaiheet kunnes homma on valmis!

Yksinkertaistettuna testivetoinen kehitys (TDD) etenee siis yllä kuvatulla tavalla. Miksi niin kannattaa toimia, kun koodin voisi vain kirjoittaa ja säästyisi aikaa sekä rahaa? Äkkiseltään sitä voisi kuvitellakin niin, mutta kun asiaa katsoo laajemmasta näkökulmasta, ymmärtää että toimivat testit voivat pelastaa monelta ongelmalta projektin elinkaaren aikana.

Kun testit on kirjoitettu hyvin, ne menevät ”helposti rikki” ja kattavat koko koodikannan. Kuvitellaan yksinkertaistettu tilanne, jossa koodimuutos tiedostossa A aiheuttaa virhetilanteen tiedostossa X, mutta vain kun A:ta kutsutaan tiedostosta X. Tässä tapauksessa koodimuutos voi näyttää toimivan oikein, kun sitä ajetaan tiedostosta A. Mikäli testit kattavat koko koodikannan, huomaamme virheen heti testatessa, koska X ei enää toimikaan oletetulla tavalla, ja ”testi-X” ei mene läpi. Tämä on vain pieni yksinkertainen esimerkki TDD:n (tai yleisellä tasolla testaamisen) hyödyistä bugien välttämiseksi, mutta saattaa pelastaa projektin monessa kohtaa 🙂

TDD ja WordPress

Kehittämö aloitti siirtymisen testivetoisen kehityksen kaltaiseen toimintatapaan tämän vuoden aikana. Suurin motivaattori oli yhteistyökumppanimme WP-palvelun tarjoama kehitysympäristö, jossa on oletuksena WP:n etusivun ja kirjautumisen toiminnan läpikäyvät RSpec-testit.

Voidaan toki kiistellä siitä, voiko RSpec-testejä hyödyntävän WP-teeman sanoa olevan testivetoista kehitystä, koska siinä ei testata varsinaisesti esim. suorittavaa koodia. Mielestäni se on sivuseikka, koska ideana on kuitenkin tehdä kehitystä ylempänä mainittujen viiden kohdan mukaan. Tässä tapauksessa kyse ei tietenkään ole ”puhtaasta TDD:stä”, koska koko koodikantaa emme pysty testaamaan tällä tavalla.

Kutsun toimintatapaamme kuitenkin TDD:n kaltaiseksi, koska tavoitteemme on kirjoittaa kaikissa uusissa WP-projekteissa niin paljon testejä (ennen koodin kirjoittamista), kuin mahdollista. Tällä tavalla pystyy kyllä testaamaan monia sivuston toiminnallisuuksia ja välttyy useilta mahdollisilta ongelmatilanteilta.

Kirjoitin ensimmäiset omat RSpec-testit jo vuosia käytössä olleeseen Kehittämön WP-multisite-projektiin. Ideana oli testata uuden tilattavan sivuston oleellisimmat vaiheet:

  • Uuden sivuston tilaaminen
  • Tilausvahvistusviestin lähettäminen / vastaanottaminen
  • Sivuston käyttöönotto ja siihen liittyvien sähköpostiviestien lähettäminen / vastaanottaminen
  • Sivuston käyttöajan ostaminen
  • Sivuston käyttöajan umpeutumiseen liittyvien sähköpostiviestien lähettäminen / vastaanottaminen

RSpec-testeissä käytetään Capybara-nimistä Ruby gemia, joka mahdollistaa sivuston testaamisen ”ihmismäisesti”. Capybara mahdollistaa mm. siirtymisen halutulle sivulle, halutun sisällön tarkistamisen sivulta, linkkien klikkaamisen ja lomakkeiden täyttämisen halutulla sisällöllä.

WP:n toimintoja testatessa suuri apu on ollut kehitysympäristön mukana tuleva WP-moduuli, jossa on oletuksena jo monia hyviä apufunktioita. Omia testejämme varten laajensin sitä mm. useilla wp-cli -komennoilla, josta esimerkkinä seuraava:


def self.testUserExists
system "wp user get #{@@randomUserEmail} > /dev/null 2>&1"
if $?.success?
return true
else
return false
end
end

Kyseistä metodia käytetään luonnollisestikin tarkistettaessa, löytyykö käyttäjää halutulla sähköpostiosoitteella kannasta. Teoriassa testien aikana voi suorittaa mitä tahansa wp-cli-komentoja.

Sähköpostiviestien tarkistaminen tehdään kehitysympäristön mukana tulevaa Mailcatcheria hyödyntäen. Mailcatcher tyhjennetään ennen testin tekemistä suorittamalla:


def clear_email
visit WP.getMailcatcherURL
click_link('Clear')
expect(page).to have_no_text WP.getRandomUserEmail
end

Ja testin aikana siirrytään Cabybaran avulla Mailcatcherin sivulle, jossa voi tarkistaa esim. onko tietyllä otsikolla tullut sähköpostia:


describe "Success emails tests" do
before do
visit WP.getMailcatcherURL
end
after (:all) do
clear_email
end
it "Check that #{WP.getMailcatcherURL}-page contains 'Uuden käyttäjän rekisteröityminen' AND 'Käyttäjätunnuksesi ja salasanasi' -emails" do
expect(page).to have_text '[Kehittamo] Uuden käyttäjän rekisteröityminen'
expect(page).to have_text 'Tervetuloa käyttämään uutta sivustoasi: ' + WP.siteurl('/') + WP.getRandomUserFirstName + WP.getRandomUserLastName
end
it "Open email that contains username and email" do
find("tr[data-message-id='1']").click
within_frame 0 do
expect(page).to have_text 'Sähköposti: ' + WP.getRandomUserEmail
end
end
it "Open email that contains user welcome message" do
find("tr[data-message-id='2']").click
within_frame 0 do
expect(page).to have_text WP.siteurl('/') + WP.getRandomUserFirstName + WP.getRandomUserLastName
expect(page).to have_text 'Käyttäjätunnuksesi on'
expect(page).to have_text WP.getRandomUserFirstName + WP.getRandomUserLastName
expect(page).to have_id 'reset_password_link'
end
end
end

Itse testit ajetaan pre-push git-hookissa feature-branchissa. Tämä sen vuoksi, että master-haarassa on vain tuotantokelpoinen ja testattu koodi, eikä master-haaraa käytetä kehityksessä. Ideana on luonnollisesti estää rikkinäisen koodin pääseminen omalta koneelta eteenpäin.

Millaisia testikäytänteitä muut käyttävät WordPressin teemojen/lisäosien kehityksessä?

Ongelmat testien kanssa

Kaikki ei suinkaan ole sujunut ongelmitta, kun olemme tehneet testejä (vanhoihin) projekteihin.

Ongelmia on aiheuttaneet mm. Pro Sites -lisäosan $psts->checkout_url(), joka heittää fatal error:ia kutsuttaessa testien aikana ja WP:n wpmu_new_blog-hook, jonka suorittamisen aikana ei ole pystynyt tekemään tietokantaan ”insert/update”-toimintoja. Tosin jälkimäinen ongelma ei suoranaisesti liity testeihin, koska tietokantakyselyt eivät toimi ”normaalissakaan” tilanteessa. Mikään alla olevista tietokantakyselyistä ei siis toimi wpmu_new_blog-hookissa:


/**
* WONTFIX:
* Any of these db-queries are not working in
* add_action( 'wpmu_new_blog', array( $this, 'function_name' ) ); -hook
*/
$wpdb->query(
$wpdb->prepare(
"
INSERT INTO {$wpdb->base_prefix}{$blog_id}_options
( option_name, option_value )
VALUES ( %s, %s )
",
$key,
$value
)
);
$wpdb->insert(
"{$wpdb->base_prefix}{$blog_id}_options",
array(
'option_name' => 'key',
'option_value' => 'value'
),
array(
'%s',
'%s'
)
);
update_blog_option( $blog_id, 'key', 'value' );

Onko muilla vastaavia kokemuksia wpmu_new_blog-hookista?

Suurin ongelma on ollut luultavasti kuitenkin ajatusmallin kääntäminen siihen, että kirjoitetaan testi ensin ja sitten vasta koodataan. Tuo prosessi on vielä ”in progress”. Olen myös huomannut, että uusiin projekteihin on paljon helpompi lähteä kirjoittamaan testejä, kun ”pöytä on vielä puhdas”.

Ongelmia on aiheuttanut myös se, että kaikki testit eivät mene läpi jokaisella kehittäjällä, vaikka koodi on täysin samaa ja tietokannassa on yhtäläinen data. Nämä ongelmat liittynevät jollain tavalla käyttämäämme kehitysympäristöön.

Yhteenveto

Toimivat testit voivat pelastaa päivän (tai viikonlopun). Testivetoista kehitystä ei ehkä voi käyttää ”puhtaasti” WordPress-kehityksessä, mutta siitä voi ottaa monia vaikutteita. Jokainen sivuston toimintaa läpikäyvä testi auttaa osaltaan välttämään ongelmatilanteita ja vähentää mahdollisia virheitä aiheuttavan koodin määrää.

Oman kokemuksen perusteella suurin este testivetoisempaan kehitykseen siirtymisessä WP-projekteissa on omien korvien välissä. Mutta osittain tietysti myös aikatauluissa ja budjeteissa sekä toimivissa työkaluissa 🙂

Janne Saarela

Janne Saarela

Koodaava projektipäällikkö, olutharrastaja

Kommentoi