behavior-driven development with jgiven

36
Behavior driven development with JGiven Achim Wiedemann | 24.10.2017

Upload: haufe-lexware-gmbh-co-kg

Post on 23-Jan-2018

137 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Behavior-Driven Development with JGiven

Behavior drivendevelopment with JGivenAchim Wiedemann | 24.10.2017

Page 2: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 2

Who am I?

Achim Wiedemann

Software developer

Joined Haufe-Lexware in April 2015

Working on lexoffice

Page 3: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 3

Sorry, but…

No Cloud

No Microservices

No Docker

No Big Data

Page 4: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 4

Agenda

Part I – Introduction to BDD

Part II – Traditional BDD tools

Part III – BDD with JGiven

Page 5: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 5

Part I – Introduction to BDD

Page 6: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 6

What is Behavior Driven Development?

Method of writing software tests

• Started around 2004 by Dan North

• TDD for business functionality

Goals for tests:

• Focus on business value

• Make writing tests simple

• Use domain specific language

• Provide value for devs and business people

• Make acceptance criteria executable

Page 7: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 7

BDD - where does it fit in?

Unit tests:

• Focus on technical components

• Traditional driver of TDD

Acceptance tests:

• Focus on business components

• Driver of BDD

• Usually UI or API tests

http://martinfowler.com/bliki/TestPyramid.html

Page 8: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 8

Acceptance criteria

States requirements

Needs to be met for implementation to be accepted

Can be written in the form:

As a [role]

I want [feature]

so that [benefit]

Example:

As a customer,

I want to withdraw cash from an ATM,

so that I don’t have to wait in line at the bank.

Involved roles User intent Business value

Page 9: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 9

Acceptance criteria – 2

Writing acceptance criteria can be hard

Don‘t fall into this trap:

As a user,

I want to withdraw cash from an ATM,

so that I can withdraw cash from an ATM

Simpler to write, but:

• Doesn‘t show the involved roles

• Doesn‘t show the business value for the user

Page 10: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 10

Validating acceptance criteria with scenarios

Specify tests against acceptance criteria

Scenarios can be derived from acceptance criteria:

As a customer,

I want to withdraw cash from an ATM,

so that I don’t have to wait in line at the bank.

Derived scenario „Account is in credit“:

Given the account is in credit

And the card is valid

And the dispenser contains cash

When the customer requests cash

Then ensure the account is debited

And ensure cash is dispensed

And ensure the card is returned

Page 11: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 11

Validating acceptance criteria with scenarios - 2

Use Given-When-Then form:

• Given [context]

When [events]

Then [expected outcomes]

Simple template

Easy to read

Easy to write

Central part of BDD

Page 12: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 12

BDD summary

Validate against AC with Scenarios

• Use Given – When – Then form

Goals for tests:

• Focus on business value

• Make writing tests simple

• Use domain specific language

• Provide value for devs and business people

• Make acceptance criteria executable

Page 13: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 13

Part II – Traditional BDD tools

Page 14: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 14

JBehave

The missing part: „Make acceptance criteria executable“

• JBehave was born

Uses a *.story file for scenarios (plain text)

Scenarios are mapped to test methods (Java)

Run scenario tests like JUnit tests

View results as report (text, HTML, XML, …)

Page 15: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 15

JBehave

Scenario: trader is not alerted below threshold

Given a stock of symbol STK1 and a threshold of 10.0

When the stock is traded at 5.0

Then the alert status should be OFF

Scenario: trader is alerted above threshold

Given a stock of symbol STK1 and a threshold of 10.0

When the stock is traded at 11.0

Then the alert status should be ON

public class TraderSteps { // look, Ma, I'm a POJO!!

private Stock stock;

@Given("a stock of symbol $symbol and a threshold of $threshold")

public void aStock(String symbol, double threshold) {

stock = new Stock(symbol, threshold);

}

@When("the stock is traded at $price")

public void theStockIsTradedAt(double price) {

stock.tradeAt(price);

}

@Then("the alert status should be $status")

public void theAlertStatusShouldBe(String status) {

ensureThat(stock.getStatus().name(), equalTo(status));

}

}

Page 16: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 16

JBehave summary

The good:

• Plain english

• Focus on business value

• Use of domain specific language

• Business people can write scenarios

The bad:

• Plain english

• String matching

• No guidance with domain specific language

• Usually devs end up writing scenarios

Page 17: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 17

Part III – BDD with JGiven

Page 18: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 18

JGiven

Created in 2014 by Jan Schäfer (TNG Technology Consulting)

Main goal:

• Remove disconnect between code and plain text in BDD

JGiven:

• Write everything in Java code

• Promote code reuse

• Leverage existing tools (JUnit)

• Great reports

Page 19: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 19

Scenario example: place order in shop

@Test

public void logged_in_customer_should_be_able_to_place_an_order() {

given()

.user_is_logged_in_as(USER_TEST.username, USER_TEST.password)

.and().user_is_on_catalog_page();

when()

.user_adds_item_to_cart(PRODUCT_ID_BOOK, 1)

.and().user_opens_cart_page()

.and().user_follows_shipping_address_link()

.and().user_enters_some_shipping_address()

.and().user_places_order();

then()

.user_should_be_on_order_success_page();

}Logged in customer should be able to place an order

Given user is logged in as test test

And user is on catalog page

When user adds item to cart 1 1

And user opens cart page

And user follows shipping address link

And user enters some shipping address

And user places order

Then user should be on order success page

Page 20: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 20

Dissecting a scenario test

public class OrderScenarioTest extends ScenarioTest<GivenStage, WhenOnCatalogPageStage, ThenStage> {

private static final String PRODUCT_ID_BOOK = "1";

@Test

public void logged_in_customer_should_be_able_to_place_an_order() {

given()

.user_is_logged_in_as(USER_TEST.username, USER_TEST.password)

.and().user_is_on_catalog_page();

when()

.user_adds_item_to_cart(PRODUCT_ID_BOOK, 1)

.and().user_opens_cart_page()

.and().user_follows_shipping_address_link()

.and().user_enters_some_shipping_address()

.and().user_places_order();

then()

.user_should_be_on_order_success_page();

}

}

Stepmethod

StageScenario

Page 21: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 21

Scenarios

Methods in normal JUnit test classes

Derive JUnit test from ScenarioTest

• given()

• when()

• then()

Specify stages as type parameters

Use snake_case for test methods for proper casing

Page 22: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 22

Scenarios - 2

public class OrderScenarioTest extends ScenarioTest<GivenStage, WhenOnCatalogPageStage, ThenStage> {

private static final String PRODUCT_ID_BOOK = "1";

@Test

public void logged_in_customer_should_be_able_to_place_an_order() {

given()

.user_is_logged_in_as(USER_TEST.username, USER_TEST.password)

.and().user_is_on_catalog_page();

when()

.user_adds_item_to_cart(PRODUCT_ID_BOOK, 1)

.and().user_opens_cart_page()

.and().user_follows_shipping_address_link()

.and().user_enters_some_shipping_address()

.and().user_places_order();

then()

.user_should_be_on_order_success_page();

}

}

:GivenStage

:WhenOnCatalogPageStage

:ThenStage

Page 23: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 23

Creating a DSL with stages

Building blocks of scenarios

Model states and actions

• States: stages

• Actions: step methods

Form DSL with a fluid interface

Given on catalog pageWhen add item to cartand view cartand order itemsand confirm orderand continue shoppingand view past ordersThen order history should not be empty

Page 24: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 24

Creating a DSL with stages - 2

Derive from Stage

• and()

• with()

• but()

Fluid interface:

• Step methods which return stages

Allows sentences like:given().user_is_on_catalog_page().and().user_adds_item_to_cart("4711", 3);

public class WhenOnCatalogPageStage extends Stage<WhenOnCatalogPageStage> {

@ScenarioState

protected CatalogPage catalogPage;

public WhenOnCatalogPageStage user_adds_item_to_cart(String productId, int quantity) {

for (int i = 0; i < quantity; i++)

catalogPage.addProductToCart(productId);

return this;

}

}

Page 25: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 25

Creating a DSL with stages - 3

public class WhenOnCatalogPageStage extends Stage<WhenOnCatalogPageStage> {

@ScenarioState

protected CatalogPage catalogPage;

@ScenarioState

protected CartPage cartPage;

@ScenarioState

protected OrderHistoryPage orderHistoryPage;

@ScenarioStage

protected WhenOnCartPageStage cartStage;

public WhenOnCatalogPageStage user_adds_item_to_cart(String productId, int quantity) {

for (int i = 0; i < quantity; i++)

catalogPage.addProductToCart(productId);

return this; // State machine: transition to same state (on catalog page)

}

public WhenOnCartPageStage user_opens_cart_page() {

cartPage.openPage();

return cartStage; // State machine: transition to different state (on cart page)

}

public void user_opnes_order_history_page() {

orderHistoryPage.openPage(); // State machine: end

}

}

Page 26: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 26

Creating a DSL with stages - 4

Model states with stages

Model actions with step methods

Naming is key: focus on business language for your DSL

public class OrderScenarioTest extends ScenarioTest<GivenStage, WhenOnCatalogPageStage, ThenStage> {

private static final String PRODUCT_ID_BOOK = "1";

@Test

public void logged_in_customer_should_be_able_to_place_an_order() {

given()

.user_is_logged_in_as(USER_TEST.username, USER_TEST.password)

.and().user_is_on_catalog_page();

when()

.user_adds_item_to_cart(PRODUCT_ID_BOOK, 1)

.and().user_opens_cart_page()

.and().user_follows_shipping_address_link()

.and().user_enters_some_shipping_address()

.and().user_places_order();

then()

.user_should_be_on_order_success_page();

}

}

Page 27: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 27

Step methods

Actions of our DSL

Make up sentences in report

Use snake_case for correct casing in report

Page 28: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 28

Sharing state

State can be shared accross stages

• Page objects

• Services

• Stages

Injection via @ScenarioState annotations

public abstract class WhenBaseStage<SELF extends WhenBaseStage<SELF>> extends Stage<SELF> {

@ScenarioState

protected LoginPage loginPage;

@ScenarioStage

protected WhenOnCatalogPageStage catalogStage;

...

public WhenOnCatalogPageStage user_logs_in_as(TestUser user) {

if (!loginPage.isUserOnPage())

loginPage.openPage();

loginPage.loginWithCredentials(user.username, user.password);

return catalogStage;

}

...

}

Page 29: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 29

Sharing state - 2

State is passed from stage to stage

Initialization best in Given stage

Alternative to @ScenarioState annotations:

• @ProvidedScenarioState / @ExpectedScenarioState

public class GivenStage extends Stage<GivenStage> {

private final PageFactoryResource pageFactory = new PageFactoryResource();

@ScenarioState

private CatalogPage catalogPage;

@ScenarioState

private LoginPage loginPage;

@ScenarioState

private CartPage cartPage;

@BeforeScenario

public void setUp() {

catalogPage = pageFactory.createCatalogPage();

loginPage = pageFactory.createLoginPage();

cartPage = pageFactory.createCartPage();

}

...

}

Page 30: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 30

Reports

Page 31: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 31

Reports - 2

Correct casing when snake_case is used in methods

• Example: item_is_registered_in_SAP()

Scenarios can be grouped via tags

• Use custom annotations to create tags

Custom formatters for improved readability

• Step methods

• Method parameters

Formats: HTML and JSON

Page 32: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 32

Tags

@IsTag(name="Critical", description="Features that are critical for the user")

@Retention(RetentionPolicy.RUNTIME)

public @interface CriticalFeature {

}

@CriticalFeature

@Test

public void added_item_appears_in_cart() {

...

}

Page 33: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 33

Formatters

Improve readability with formatters

• Use $ in method names

• Custom formatters via @Format

Code:

when().user_adds_item_to_cart("4711", 3)

Generated report:

When user adds item to cart 4711 3

Code (using placeholders):

when().user_adds_item_$_to_cart_of_quantity_$("4711", 3)

Generated report (using placeholders):

When user adds item 4711 to cart of quantity 3

Page 34: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 34

Formatters - 2

@Test

public void added_item_appears_in_cart() {

given()

.user_is_logged_in_as(USER_TEST);

when()

.user_adds_item_$_to_cart_of_quantity_$(PRODUCT_ID_BOOK, 1)

.and().user_opens_cart_page();

then()

.user_should_see_cart_item_count_of(1);

}

Page 35: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 35

JGiven - Summary

The good:

• Simple JUnit tests

• Pure Java – no mapping from text to code

• Business focused DSL

• Developer friendly

The bad:

• Documentation quite good but could be even better

• Relatively unknown

• Business people can‘t write scenarios

Page 36: Behavior-Driven Development with JGiven

24.10.2017 Achim WiedemannSeite 36

DEMO

https://github.com/achwie/hystrix-demo