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>