acceptance test driven development
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 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
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