Straight from the horse's mouth:
#BDD in a tweet: Using examples at multiple levels to create a shared understanding and surface uncertainty to deliver software that matters
— Dan North (@tastapod) May 26, 2013Using Examples
at multiple levels
to create a shared understanding
and surface uncertainty
to deliver software
that matters!
An Orphan
Marketing Hype
ATDD (Acceptance Test Driven Development)
Lost it's Zing!!!
Cucumber - the worlds most misunderstood collaboration tool: https://t.co/BolhAB9D8L
— Cucumber Pro (@cucumber_pro) March 3, 2014TDD Evolved
Inspires Collaboration (3 Amigos)
Behaviour vs Implementation
Outside In development
Living Documentation
Abundance of Tools
Simples!
JBehave
Concordian
Fitnesse
EasyB
Spock?
Jasmine (for JavaScript)
Cucumber
repositories {
jcenter()
}
dependencies {
compile 'org.codehaus.groovy:groovy:2.3.6'
testCompile 'junit:junit:4.11'
testCompile 'info.cukes:cucumber-groovy:1.1.8'
testCompile 'info.cukes:cucumber-junit:1.1.8'
}
build.gradle
import cucumber.api.CucumberOptions
import cucumber.api.junit.Cucumber
import org.junit.runner.RunWith
@RunWith(Cucumber)
@CucumberOptions(
format=["pretty", "html:build/reports/cucumber"],
strict=true,
features=["features"],
glue=["src/test/steps"]
)
public class RunCukesTest { }
src/test/groovy/RunCukeTests.groovy
Feature: Calculate
Scenario: Add two numbers
Given the input "2+2"
When the calculator is run
Then the output should be "4"
Scenario: Subtract two numbers
Given the input "9-4"
When the calculator is run
Then the output should be "5"
src/test/cucumber/adding.feature
import static cucumber.api.groovy.EN.*
Given(~'^the input "([^"]*)"$') { String input ->
//some groovy code
}
When(~'^the calculator is run$') { ->
//some groovy code
}
Then(~'^the output should be "([^"]*)"$') { String output ->
//some groovy code
}
src/test/cucumber/steps/add_steps.groovy
import static cucumber.api.groovy.Hooks.*
Before(){
//some groovy code
}
After(){
//some groovy code
}
src/test/cucumber/support/env.groovy
Before and After each Scenario
dependencies {
...
test "org.codehaus.geb:geb-junit4:0.7.2"
test "org.seleniumhq.selenium:selenium-chrome-driver:2.32.0"
test "org.seleniumhq.selenium:selenium-support:2.32.0"
...
}
plugins {
...
test ':cucumber:1.0.1'
compile ':remote-control:1.5'
...
}
grails-app/conf/BuildConfig.groovy
cucumber {
tags = ["~@wip"]
features = ["test/cucumber"]
glue = ["test/steps"]
formats = [ "html:target/test-reports/cucumber" ]
strict = true
}
grails-app/conf/CucumberConfig.groovy
Replaces Test Runner configuration.
Feature: Invader Quotes
Scenario: Invader Zim quotes by Name
Given an Invader named "Zim"
And the Invader "Zim" says "The Earth is safe! I did it, GIR! Now let's go destroy it!"
When a Quote is requested for "Zim"
Then we are taken to the Quote Page
And we see "The Earth is safe! I did it, GIR! Now let's go destroy it!"
Scenario: Invader GIR quotes by Name
Given an Invader named "GIR"
And the Invader "GIR" says "Can I be a mongoose dog?"
When a Quote is requested for "GIR"
Then we are taken to the Quote Page
And we see "Can I be a mongoose dog?"
test/cucumber/quote.feature
Given(~'^an Invader named "([^"]*)"$') { String name ->
//persist quote
}
When(~'^a Quote is requested for "([^"]*)"$') { String name ->
to QuotePage
}
Then(~'^we see "([^"]*)"$') { String quote ->
def invasionQuote = page.fetchInvasionQuote()
assert invasionQuote == quote
}
test/cucumber/steps/quote_steps.groovy
Gorm no longer supported!
Use Remote Control Plugin instead!
Use Geb!
class Quote {
String name
String message
}
grails-app/domain/../Quote.groovy
import geb.Page
class QuotePage extends Page {
static url = "/zim-grails/invader"
static at = { title == "Invader Zim Quotes" }
static content = {
quote { $("#message") }
}
def fetchInvasionQuote(){
quote.text()
}
}
test/functional/page/QuotePage.groovy
import geb.binding.BindingUpdater
import geb.Browser
import static cucumber.api.groovy.Hooks.After
import static cucumber.api.groovy.Hooks.Before
System.setProperty("geb.build.baseUrl", "http://localhost:8080")
Before () {
bindingUpdater = new BindingUpdater (binding, new Browser ())
bindingUpdater.initialize ()
}
After () {
bindingUpdater.remove ()
}
test/cucumber/support/env.groovy
class InvaderController {
def index(String id) {
[quote: Quote.findByName(id)]
}
}
"/invader/$name"(controller: "invader")
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<meta name="layout" content="main"/>
<title>Invader Zim Quotes</title>
</head>
<body>
<blockquote class="bq1" id="message">
<p>${quote.message}</p>
</blockquote>
<p class="after" id="name">--Invader ${quote.name}</p>
</body>
</html>