acceptance test driven development

39
Acceptance Test Driven Development Bringing Testers and Developers Together John Ferguson Smart Wakaleo Consulting Ltd. http://www.wakaleo.com Email: [email protected] Twitter: wakaleo

Upload: wakaleo-consulting

Post on 20-Aug-2015

4.534 views

Category:

Technology


0 download

TRANSCRIPT

Acceptance Test Driven Development

Bringing Testers and Developers Together

John Ferguson SmartWakaleo Consulting Ltd.http://www.wakaleo.comEmail: [email protected]: wakaleo

Introduction

So what’s this talk about, anywayAcceptance tests as a communication tool

Acceptance Test Driven Development

BDD-style Acceptance Tests - easyb

Acceptance Tests

Acceptance Tests - a key Agile practiceA communication tool

Owned by the customer

Determine when a feature is ‘done’

Written together (customer, developer, tester)

Focus on What, not How

Acceptance Tests

Acceptance Tests - how far do you go?In depth tests or examples of system usage?

Exhaustive Tests or Sample Stories?

Acceptance Tests

Acceptance Tests - a key Agile practice

User Story 1 - Calculate my tax rate

As a tax payer, I want to be able to calculate my tax online, so that I can put enough money aside.

User Story 1 - Calculate my tax rate

As a tax payer, I want to be able to calculate my tax online, so that I can put enough money aside.

User Story 1 - Transfer funds

As a bank client, I want to transfer funds from my current account to my savings account, so that I can earn more interest

So how do we know when this feature is done?

Acceptance Tests

Acceptance Tests - a key Agile practice

User Story 1 - Calculate my tax rate

As a tax payer, I want to be able to calculate my tax online, so that I can put enough money aside.

User Story 1 - Calculate my tax rate

As a tax payer, I want to be able to calculate my tax online, so that I can put enough money aside.

User Story 1 - Transfer funds

As a bank client, I want to transfer funds from my current account to my savings account, so that I can earn more interest

User Story 1 - Acceptance Tests

- Client can transfer money between two accounts- Client can’t transfer negative amount- Client can’t transfer more than current account balance- Client can’t transfer from a blocked account

So how do we know when this feature is done?

Let’s write some Acceptance Criteria

Acceptance Tests

Acceptance CriteriaConditions that must be met before the story is complete

Provided by the customer

Some folks use a more formal notation

How do I get my tests? Just add some examples!

User Story 1 - Acceptance Tests

- Client can transfer money between two accounts- Client can’t transfer negative amount- Client can’t transfer more than current account balance- Client can’t transfer from a blocked account

Acceptance Test-Driven Development

Acceptance Tests drive work during the iteration

Iteration n-1 Iteration n Iteration n+1

Pick a story card

Write the acceptance tests

Automate the acceptance tests

Implement the user story

Acceptance Test-Driven Development

Implementing/Automating the Acceptance TestsAcceptance Tests become ‘executable’

Focus is still on communication

You don’t need to use the same language as the application

Tools for the job

What tools exist? Two main approachesNarrative

easyb, JBehave, rspec, Cucumber,...

Table-based

Fitnesse,...

So what is easyb, anyway?A BDD testing framework for Java

Make testing clearer and easier to write

Make tests self-documenting

Help developers focus on the requirements

Based on Groovy

Java-like syntax

Quick to write

Full access to Java classes and APIs

Well-suited to Acceptance Tests

Introducing easyb

BDD Acceptance Testing

Easyb supports Specifications and StoriesSpecifications express requirements as simple statements

Stories use the “given-when-then” approach

Easyb in Action

Writing Easyb SpecificationsSimple and informal

Easy to write

Very similar to acceptance criteria

Easyb Specifications

Easyb Specifications

Writing Easyb Specifications

User Story 1 - Acceptance Tests

- Client can transfer money between two accounts- Client can’t transfer negative amount- Client can’t transfer more than current account balance- Client can’t transfer from a blocked account

Start off with our acceptance criteria

description "A client should be able to transfer money between accounts"

it "should let a client transfer money from a current to a savings a/c"it "should not allow a client to transfer a negative amount"it "should not allow a client to transfer more than the current balance"it "should not allow a client to transfer from a blocked account"

Express these in Easyb

AccountTransfer.specifications

Easyb Specifications

Writing Easyb SpecificationsExecutable Requirements

description "A client should be able to transfer money between accounts"

it "should let a client transfer money from a current to a savings a/c"it "should not allow a client to transfer a negative amount"it "should not allow a client to transfer more than the current balance"it "should not allow a client to transfer from a blocked account"

This code will run!

The tests are marked as ‘PENDING’

Easyb Specifications

Writing Easyb SpecificationsHTML Progress reports

Easyb Specifications

Writing Easyb SpecificationsImplementing the testspackage com.wakaleo.accounts.domain

description "A client should be able to transfer money between accounts"

it "should let a client transfer money from a current to a savings a/c", { current = new Account(200) savings = new Account(300) current.transferTo(savings, 50) savings.balance.shouldBe 350 current.balance.shouldBe 150 }it "should not allow a client to transfer a negative amount"it "should not allow a client to transfer more than the current balance"it "should not allow a client to transfer from a blocked account"

A developer implements the test in Groovy

No longer pending

Still pending...

Writing Easyb StoriesUse a narrative approach

Describe a precise requirement

Can be understood by a stakeholder

Usually made up of a set of scenarios

Use an easy-to-understand structure:

Given [a context]...

When [something happens]...

Then [something else happens]...

Easyb Stories

Easyb Stories

Building an easyb storyA story is made up of scenarios

Scenarios validate specific behaviour

User Story 1 - Acceptance Tests

- Client can transfer money between two accounts- Client can’t transfer negative amount- Client can’t transfer more than current account balance- Client can’t transfer from a blocked account

scenario "A client can transfer money from a current to a savings a/c"scenario "A client is not allowed to transfer a negative amount"scenario "A client is not allowed to transfer more than the current balance"scenario "A client is not allowed to transfer from a blocked account"

AccountTransfer.story

Easyb Stories

Anatomy of an easyb story

“Scenario”: corresponds to precise requirement

“Given”: the context in which this requirement applies

“When”: An event or action

“Then”: The expected results of this action

scenario "A client can transfer money from a current to a savings a/c", { given 'a current a/c with $200 and a savings a/c with $300' when 'you transfer $50 from the current a/c to the savings a/c' then 'the savings a/c should have $350 and the current a/c $150'}

Implementing the scenario

Easyb Stories

package com.wakaleo.accounts.domain

scenario "A client can transfer money from a current to a savings a/c", { given 'a current a/c with $200 and a savings a/c with $300', { current = new Account(200) savings = new Account(300) } when 'you transfer $50 from the current a/c to the savings a/c', { current.transferTo(savings, 50) } then 'the savings a/c should have $350 and the current a/c $150', { savings.balance.shouldBe 350 current.balance.shouldBe 150 } }

scenario "A client is not allowed to transfer a negative amount"scenario "A client is not allowed to transfer more than the current balance"scenario "A client is not allowed to transfer from a blocked account"

Implementing the scenario - an alternative solution

Easyb Stories

package com.wakaleo.accounts.domain

scenario "A client can transfer money from a current to a savings a/c", { given 'a current a/c with $200', { current = new Account(200) } and 'a savings a/c with $300', { savings = new Account(300) } when 'you transfer $50 from the current a/c to the savings a/c', { current.transferTo(savings, 50) } then 'the savings a/c should have $350', { savings.balance.shouldBe 350 } and 'the current a/c should have $150', { current.balance.shouldBe 150 }}scenario "A client is not allowed to transfer a negative amount"scenario "A client is not allowed to transfer more than the current balance"scenario "A client is not allowed to transfer from a blocked account"

Using ‘and’ for more clarity

Ensuring what should beThe shouldBe syntax:

Intuitive, readable and flexible

Comes in many flavors

Easyb assertions

account.balance.shouldBe initialAmount

account.balance.shouldBeEqualTo initialAmount

account.balance.shouldNotBe 0

account.balance.shouldBeGreaterThan 0

account.shouldHave(balance:initialAmount)

Implementing another scenario - error conditions

Easyb Stories

package com.wakaleo.accounts.domain

scenario "A client can transfer money from a current to a savings a/c", { ...}scenario "A client is not allowed to transfer a negative amount", { given 'a current a/c with $200', { current = new Account(200) } and 'a savings a/c with $300', { savings = new Account(300) } when "you try to transfer a negative amount", { transferNegativeAmount = { current.transferTo(savings, -50) } } then "an IllegalTransferException should be thrown", { ensureThrows(IllegalTransferException.class) { transferNegativeAmount() } }}scenario "A client is not allowed to transfer more than the current balance"scenario "A client is not allowed to transfer from a blocked account"

Create a closure representing this operation

Fail if the exception is not thrown

Easyb fixtures

Structuring your tests...fixtures in easybSetting up the test environment...

Similar to JUnit fixtures @Before and @BeforeClass

before is run at the start of the whole story

before_each is run before each scenario

Useful for setting up databases, test servers, etc.

Refactoring our scenarios - before and before_each

Easyb fixtures

before_each "setup the test accounts", { given 'a current a/c with $200', { current = new Account(200) } and 'a savings a/c with $300', { savings = new Account(300) } }

scenario "A client can transfer money from a current to a savings a/c", { when 'you transfer $50 from the current a/c to the savings a/c', { current.transferTo(savings, 50) } then 'the savings a/c should have $350 and the current a/c $150', { savings.balance.shouldBe 350 } and 'the current a/c should have $150', { current.balance.shouldBe 150 }}

scenario "A client is not allowed to transfer a negative amount", { when "you try to transfer a negative amount", { transferNegativeAmount = { current.transferTo(savings, -50) } } then "an IllegalTransferException should be thrown", { ensureThrows(IllegalTransferException.class) { transferNegativeAmount() } }}

This will be done before each scenario

Easyb fixtures

Shared behaviourRefactor common code in easyb scenariosshared_behavior "shared behaviors", {  given "a string", {    var = ""  }  when "the string is hello world", {    var = "hello world"  }}

scenario "first scenario", {  it_behaves_as "shared behaviors"    then "the string should start with hello", {    var.shouldStartWith "hello"  }}

scenario "second scenario", {  it_behaves_as "shared behaviors"    then "the string should end with world", {    var.shouldEndWith "world"  }}

Common behavior (‘shared_behavior’)

Reused here (‘it_behaves_as’)...

...and here

Web testing with easyb

Easyb is convenient for web testingBDD/Functional tests

Run against a test server, or use Jetty

Use your choice of web testing frameworks

Selenium

JWebUnit

...

Web testing with easyb

Many options - let’s look at twoSelenium

Runs in a browser

High-level API

Runs slower and more work to set up

JWebUnit

Simulates a browser

Runs faster, easy to set up

API slightly lower level

Web testing with easyb

An example - writing functional tests with JWebUnitimport net.sourceforge.jwebunit.junit.WebTester

before_each "initialize a web test client", { given "we have a web test client", { tester = new WebTester() tester.setBaseUrl("http://localhost:8080/tweeter-web") }}

scenario "User signup should add a new user", { when "I click on the sign up button on the home page", { tester.beginAt("/home") tester.clickLinkWithText("Sign up now!") } and "I enter a new username and password", { tester.setTextField("username", "jane") tester.setTextField("password", "tiger") tester.submit() } then "the application should log me on as the new user and show a welcome message", { tester.assertTextPresent("Hi jane!") }}

Set up a JWebUnit client

Click on a link

Enter some values

Check the results

The easyb HTML report

Easyb reports

Test results summary

Failed stories

Unimplemented stories

The easyb HTML report

Easyb reports

Test results summary

Test failure details

Unimplemented stories

How does easyb compare with other tools?Cucumber, RSpec (Ruby) - very similar to easyb

FitNesse - wikis and tables

Concordion - marked-up HTML

Other Approaches

FitNesse - wiki-based acceptance testsTest data is written at tables on a Wiki

Java classes implement the tests behind the scenes

FitNesse

FitNesse - wiki-based acceptance tests

FitNesse

Test data and scenarios as a table

Test data Expected results

FitNesse - wiki-based acceptance tests

FitNesse

Testers can write/edit the Wiki pages

You can also import to and from Excel

FitNesse - wiki-based acceptance tests

FitNesse

public class TransferMoneyBetweenAccounts { private BigDecimal savingsBalance; private BigDecimal currentBalance; private BigDecimal transfer; private BigDecimal finalSavingsBalance; private BigDecimal finalCurrentBalance; private boolean exceptionThrown; public void setSavingsBalance(BigDecimal savingsBalance) {...} public void setCurrentBalance(BigDecimal currentBalance) {...} public void setTransfer(BigDecimal transfer) {...} public BigDecimal finalCurrentBalance() {...} public BigDecimal finalSavingsBalance() {...} public boolean exceptionThrown() {...}

public void execute() { Account currentAccount = new Account(currentBalance); Account savingsAccount = new Account(savingsBalance); exceptionThrown = false; try { currentAccount.transferTo(savingsAccount, transfer); finalCurrentBalance = currentAccount.getBalance(); finalSavingsBalance = savingsAccount.getBalance(); } catch (IllegalTransferException e) { exceptionThrown = true; } }}

Tests are implemented by Java classes

Each column has a field in the class

Expected results have getters

Performing the test

There are also some commercial tools out there...GreenPepper

Supports tables and BDD-style

Nice tool integration (Confluence, Maven,...)

Twixt

Thoughtworks product, focus on web testing

Others?

Commercial options?

Thank You

John Ferguson SmartWakaleo Consulting Ltd.http://www.wakaleo.comEmail: [email protected]: wakaleo