layout | title | inheader | permalink |
---|---|---|---|
page |
Cucumber |
false |
/cucumber/ |
Cucumber on user storyjen hyväksymistestauksen automatisointiin tarkoitettu kirjasto/työkalu.
Ideana on kirjoittaa storyjen testit asiakkaan ymmärtävässä muodossa, luonnollisella kielellä, mutta tehdä niistä kuitenkin automaattisesti suoritettavia.
Cucumber on behavior-driven development eli BDD -koulukunnan kehittämä kirjasto. BDD:ssä pyritään välttämään termin testi käyttöä ja sen sijaan puhutaan esimerkkitoiminnallisuuksista tai skenaarioista.
Vaikka Cucumber on varsinaisesti tarkoitettu koko ohjelmiston "end-to-end"-testaukseen, tarkastellaan ensin Cucumberin toimintaperiaatteita testaamalla yksittäistä luokkaa.
Hae kurssirepositorion hakemistossa viikko3/HelloCucumber oleva projekti.
Testattavana on yksinkertainen laskuri:
public class Counter {
int val = 0;
public void increase() {
val++;
}
public void reset() {
val = 0;
}
public void increment(int a) {
val += a;
}
public int value() {
return val;
}
}
Laskurin haluttua toiminnallisuutta kuvaavat seuraavat user storyt
- As a user I want to be able to increase the counter value
- As a user I want to be able to set the counter to value zero
Cucumberissa (ja muutamassa muussakin BDD-työkaluissa) vaatimukset ilmaistaan Gherkin-formaatissa. User storyista, eli käyttäjän haluamista toiminnallisuuksista käytetään nimitystä feature. Laskimen storyt voidaan ilmaista seuraavasti:
Feature: As a user I want to be able to increase the counter value
Feature: As a user I want to be able to set the counter to value zero
Jokainen feature talletetaan omaan .feature-päätteiseen tiedostoon. Featuret sijoitetaan gradle-projekteissa hakemiston src/test/resources/ alle. Esimerkkiprojektissa featuret ovat tiedostoissa src/test/resources/ohtu/increasingCounter.feature ja src/test/resources/ohtu/resetingCounter.feature
Featureen liittyy joukko skenaarioita, jotka vastaavat käytännössä storyn hyväksymistestejä:
Feature: As a user I want to be able to increase the counter value Scenario: Increment once Given Counter is initialized When it is incremented Then the value should be 1 Scenario: Increment by many Given Counter is initialized When it is incremented by 5 Then the value should be 5 Scenario: Increment many times Given Counter is initialized When it is incremented And it is incremented And it is incremented Then the value should be 3
Skenaariot taas kirjoitetaan Given, When, Then -formaatissa. Jokaista skenaarion riviä kutsutaan askeleeksi eli stepiksi.
- Given kertoo skenaarion (eli testin) lähtötilanteen
- When kuvaa operaation mitä skenaariossa testataan
- Then ilmaisee mitä skenaariossa pitäisi tapahtua
Avainsanan And avulla jokaiseen skenaarion askeleista voidaan liittää useita steppejä. Näin tehdään esimerkin kolmannessa skenaariossa.
Jotta testeistä saadaan suorituskelpoisia, tulee projektiin kirjoittaa skenaarion steppejä vastaava koodi. Jokainen steppi määritellään omana metodina luokassa Stepdefs. Esimerkin steppien määrittely tapahtuu seuraavasti:
public class Stepdefs {
Counter counter;
@Given("Counter is initialized")
public void counterIsInitialized() {
counter = new Counter();
}
@When("it is incremented")
public void itIsIncremented() {
counter.increase();
}
@Then("the value should be {int}")
public void theValueShouldBe(Integer val) {
assertEquals(val.intValue(), counter.value());
}
@When("it is incremented by {int}")
public void itIsIncrementedBy(Integer val) {
counter.increment(val);
}
}
Jokaista metodia edeltää annotaatio, joka määrittelee mitä steppiä vastaavasta metodista on kyse. Kaikkien skenaarioiden Given-step on sama, se määrittelee että skenaariot alkavat laskurin luomisella
@Given("Counter is initialized")
public void counterIsInitialized() {
counter = new Counter();
}
Stepeissä voi olla "parametreja", eli skenaariossa
Scenario: Increment by many Given Counter is initialized When it is incremented by 5 Then the value should be 5
määritellyt luvut välitetään niitä vastaaville metodeille parametrina:
@When("it is incremented by {int}")
public void itIsIncrementedBy(Integer val) {
counter.increment(val);
}
@Then("the value should be {int}")
public void theValueShouldBe(Integer val) {
assertEquals(val.intValue(), counter.value());
}
Onnistumisen varmistava Then-step suorittaa tarkastuksen JUnitin assertEquals-metodia käyttäen.
Cucumber edellyttää vielä pienen määrän konfiguraatiota, joka on tehty tiedostossa src/.../RunCucumberTest.java. Konfiguraatio on yksinkertainen, se määrittelee mistä hakemistosta feature-tiedostot löytyvät, sekä sen että testien tulos raportoidaan komentorivillä pretty
-formatterin avulla:
@RunWith(Cucumber.class)
@CucumberOptions(
plugin = "pretty",
features = "src/test/resources/ohtu",
snippets = SnippetType.CAMELCASE
)
public class RunCucumberTest {}
Määrittely on sikäli hämmentävä, että määriteltävä luokka RunCucumberTest ei sisällä mitään koodia, ja kaikki oleellinen määrittely tapahtuu luokkaan liitetyn annotaation @CucumberOptions parametreina.
Testit suoritetaan komennolla gradle test.
Huomaa, että testien suorittaminen ei todennäköisesti toimi NetBeansin testinapilla.
Laskimen nollaamiseen liittyvä story on tiedostossa src/test/resources/ohtu/resetingCounter.feature
Lisää storyyn seuraavat skenaariot:
Feature: As a user I want to be able to set the counter to value zero Scenario: Resetting after one increment Given Counter is initialized When it is incremented And it is reset Then the value should be 0 Scenario: Resetting after incrementing with several values Given Counter is initialized When it is incremented by 5 And it is reset Then the value should be 0
Kun nyt suoritat testit komennolla gradle test näyttää tulos seuraavalta:
![]({{ "/images/lh3-5.png" | absolute_url }}){:height="350px" }
Cucumber maalaa stepin And it is reset keltaisella ja kertoo virheestä
io.cucumber.junit.UndefinedStepException
Cucumber siis ilmoittaa osan stepein olevan määrittelemätön. Avaamalla selaimella virheilmoituksen tarkempi raportti, näyttää Cucumber valmiin metodirungon, jonka avulla stepin voi toteuttaa:
![]({{ "/images/lh3-6.png" | absolute_url }}){:height="350px" }
Kopioi stepin koodirunko
@When("it is reset")
public void itIsReset() {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
luokkaan Stepdefs ja täydennä se järkevällä tavalla.
Varmista että testit toimivat.