vx company
menu
sluiten
terug naar overzicht

02/05/16

Insight insight

Java Testen
sander verbruggen, vx company

Sander Verbruggen

+31 35 539 09 09


02/05/16

Cucumber voor unit testing

Cucumber wordt vaak gebruikt voor webtesting (i.c.m. Selenium). Ik focus op Cucumber en puur Java en wil je laten zien waarom ik zo enthousiast ben over Cucumber voor unit testing.

Cucumber introductie

Cucumber is een framework voor Behavior Driven Development (BDD). Cucumber gebruikt gewone taal om specificaties vast te leggen. Vanuit die specificaties kun je eenvoudig code genereren die je helpt om testen voor specificaties te schrijven. Want TDD en BDD gaan prima samen! Voorbeeldje: [text gutter=”false”] Feature: Simple math Scenario: Addition Given the number 1 And we add 1 When calculating Then the result should be 2 [/text] Leesbaar, zelfs voor je oma (en als ze geen Engels kan lezen, het kan ook volledig in het Nederlands!). Ik ga niet heel Cucumber uitleggen. Simpel gezegd:

  • Een feature (functionaliteit) is een verzameling van een of meer Scenarios
  • Een regel (feature step) begint met een keyword (Given, When, Then, …) en daarachter vrije tekst
  • Nummers en tekst tussen “quotes” worden automatisch herkend als testparameters

Voor dit voorbeeld heb je een simpel maven project nodig. Op de site van Cucumber zie je hoe je dit doet. Sla het Feature/Scenario voorbeeld op in src/test/resources/bdd/math/math.feature.

Feature class

Deze feature willen we implementeren in een Java class. Maak in dit geval een Java class in src/test/java/math/MathFeature.java. Daar gaan we zometeen mee verder.

Test class

Om deze feature met JUnit testen te laten draaien, moeten we een Test Class maken met de annotations @RunWith en @CucumberOptions. Dit mag volgens Cucumber niet de feature class zijn (probeer maar eens, Cucumber geeft hier een zinnige foutmelding op). Maak dus een (verder lege) test class aan:

[java gutter="false"]
 @RunWith(Cucumber.class)
 @CucumberOptions(features = "src/test/resources/bdd/math")
 public class MathFeatureTest {
 }
 [/java]
The feature class implementeren

Nu kunnen we de unit test eindelijk draaien! Doe dat voordat je de feature class gaat implementeren, want Cucumber helpt je hier enorm door in de output de code te plaatsen die je kunt copy/pasten naar de feature class!

[java gutter="false"]
 public class MathFeature {
 @Given("^the number (\\d+)$")
 public void the_number(int number) throws Throwable {
 }

@Given("^we add (\\d+)$")
 public void we_add(int number) throws Throwable {
 }

@When("^calculating$")
 public void calculating() throws Throwable {
 }

@Then("^the result should be (\\d+)$")
 public void the_result_should_be(int expectedResult) throws Throwable {
 }
 }
 [/java]

Met dit skelet kun je de unit test verder in gaan vullen, dat valt buiten de scope van deze post.

Stapje verder

Met unit testen zie je vaak hetzelfde scenario met steeds nieuwe parameters. Je kunt natuurlijk het scenario meerdere keren copy/pasten en vullen met andere getallen. In Java zou je dat refactoren. JUnit heeft hiervoor parameterized tests. In Cucumber kun je dit ook refactoren, namelijk met abstracte scenario’s:

[text gutter="false"]
 Scenario Template:
 Given the number <base>
 And we add <addition>
 Then the result should be <expected result>

Examples:
 | base | addition | expected result |
 | 1 | 1 | 2 |
 | 2 | 2 | 4 |
 | 100 | 100 | 200 |
 [/text]

Het scenario bevat parameters tussen < en > en heeft een tabel met voor elke parameter een kolom. Elke regel is een afzonderlijke testgeval. Handig: Eclipse en IntelliJ hebben Cucumber plugins die zowel de teksten als de tabellen formatteren! Geen lamme spatiebalk-duim dus!

Mockito

Mockito is een veelgebruikt mocking framework. Als je in je feature class mocking annotations wilt gebruiken, dan moet je Mockito zelf initialiseren, omdat we de @RunWith al hebben gebruikt voor Cucumber:

[java gutter="false" highlight="8-11"]
 public class OtherFeature {
 @InjectMocks
 private SuperService service;

@Mock
 private SomeDependency dependency

@Before
 public void init() {
 MockitoAnnotations.initMocks(this);
 }
 }
 [/java]

Pas op! Je moet hier de @Before annotation uit cucumber.api.java gebruiken, niet uit org.junit!

Java 8

In Java 8 kun je je feature class ook anders implementeren. Helaas zijn er wat problemen ontstaan met deze implementatie dus daar zullen we nog even op moeten wachten…

Waarom Cucumber unit tests?

Ik zie een aantal voordelen van de combinatie BDD/TDD/Unit tests:

  • TDD to the max! Je kunt beginnen zonder een regel code. Run tekst en schrijf (test)code tot je teKst slaagt.
  • Je unit tests worden gestructureerder. Je herstructureert en herbruikt zinsconstructies i.p.v. continue je code refactoren. Je testcode heeft dus meteen een betere structuur.
  • Iemand die jouw tests leest is er sneller in thuis.
  • Laat een ontwerper/tester zelf tests toevoegen om te kijken wat er gebeurt.

Met deze handvatten kun je een start maken met het schrijven van unit tests die voor iedereen leesbaar zijn. Natuurlijk geldt net als altijd: je hoeft niet al je unit tests op deze manier te schrijven. Begin er gewoon mee en laat me weten wat je er van vindt!

Delen

Meer weten over dit onderwerp?

sander verbruggen, vx company
Neem contact op met Sander Verbruggen
gang van het kantoor, vx company