bdd de aceptación con ruby

Post on 14-May-2015

2.526 Views

Category:

Entertainment & Humor

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

Presented at AgileSpain 2010 in Madrid by Luismi Cavallé and Sergio Gil

TRANSCRIPT

Sergio Gil y Luismi Cavallé

Pruebas BDD de Aceptación con Ruby

Agile Spain 2010

Historias de Usuario

[A story] has to be a description of a requirement and its business benefit, and a set of criteria by which we all

agree that it is “done”

– Dan North

http://blog.dannorth.net/whats-in-a-story/

Anatomía de una historia

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ When I press the coffee button Then I should be served a coffee

Narrativa

Criterio de Aceptación

Anatomía de una historia

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ When I press the coffee button Then I should be served a coffee

Narrativa

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Título

Narrativa

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Rol¿Quién?

Narrativa

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Funcionalidad¿Qué?

Narrativa

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Beneficio¿Por qué?

Narrativa

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Regla de los 5 porqués

Regla de los 5 porqués

ENSÉÑAME LA PASTA !!!!

Criterio de Aceptación

Scenario: Buy last coffee

Given there are 1 coffees left in the machine And I have deposited 1$

When I press the coffee button

Then I should be served a coffee

Contexto

Criterio de Aceptación

Scenario: Buy last coffee

Given there are 1 coffees left in the machine And I have deposited 1$

When I press the coffee button

Then I should be served a coffee

Eventos

Criterio de Aceptación

Scenario: Buy last coffee

Given there are 1 coffees left in the machine And I have deposited 1$

When I press the coffee button

Then I should be served a coffee

Resultados

Criterio de Aceptación

Scenario: Buy last coffee

Given there are 1 coffees left in the machine And I have deposited 1$

When I press the coffee button

Then I should be served a coffee

Criterio de Aceptación

Scenario: Buy last coffee

Given there are 1 coffees left in the machine And I have deposited 1$

When I press the coffee button

Then I should be served a coffee

DONE

Beneficio para el negocio

TestersProgramadores

ManagersClientesAnalistas

Beneficio para el negocio

Historia de UsuarioToken de comunicación / discusiónPriorización según beneficio / valor

Criterio común de “terminado”

TestersProgramadores

ManagersClientesAnalistas

Cucumber

Cucumber

Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times

Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ When I press the coffee button Then I should be served a coffee

Pasos

Given a user “john” with password “secret”

When he tries to login as “james”

Then he should see an error message

Definición de pasos

Given a user “john” with password “secret”

Definición de pasos

Given /^a user "(.+)" with password "(.+)"$/ do |login, password|

Given a user “john” with password “secret”

Definición de pasos

Given /^a user "(.+)" with password "(.+)"$/ do |login, password| User.create! :login => login, :password => passwordend

Given a user “john” with password “secret”

Definición de pasos

When he tries to login as “james”

Definición de pasos

When /^he tries to login as "(.+)"$/ do |login|

When he tries to login as “james”

Definición de pasos

When /^he tries to login as "(.+)"$/ do |login| visit "/login" fill_in "Username", :with => login fill_in "Password", :with => "secret" click_button "Login"end

When he tries to login as “james”

Definición de pasos

Then he should see an error message

Definición de pasos

Then he should see an error message

Then /^I should see an error message$/ do

Definición de pasos

Then he should see an error message

Then /^I should see an error message$/ do assert page.has_content?("Invalid credentials")end

Demo

http://vimeo.com/12441999

+40 Idiomas

# language: esCaracterística: Búsqueda de cursos Para asegurar el mejor uso de los cursos Los estudiantes potenciales deberían poder buscar cursos Escenario: Búsqueda por materia Dado que hay 240 cursos, ninguno sobre "biología" Y hay 2 cursos A001, B205 sobre "biología" Cuando busco por "biología" Entonces debería ver los siguientes cursos: | Código de curso | | A001 | | B205 |

Otros lenguajes package cukes;

import cuke4duke.Given;import java.util.List;import java.util.ArrayList;

public class BellySteps { private List<String> belly = new ArrayList<String>();

@Given("I have (\\d+) cukes in my belly") public void bellyCukes(int cukes) { for(int i = 0; i < cukes; i++) { belly.add("cuke " + i); } }}

Otros lenguajes package cukes;

import cuke4duke.Given;import java.util.List;import java.util.ArrayList;

public class BellySteps { private List<String> belly = new ArrayList<String>();

@Given("I have (\\d+) cukes in my belly") public void bellyCukes(int cukes) { for(int i = 0; i < cukes; i++) { belly.add("cuke " + i); } }}

(Given #"^I have entered ([\d.]+) into the calculator$" (fn [number] (push-number (Float. number))))

Más funcionalidades...

Background

Feature: Multiple blog support Background: Given a global administrator named "Greg" And a blog named "Greg's anti-tax rants" And a customer named "Dr. Bill" And a blog named "Expensive Therapy" owned by "Dr. Bill" Scenario: Dr. Bill posts to his own blog Scenario: Dr. Bill tries to post to somebody else's blog Scenario: Greg posts to a client's blog

Tags

$ cucumber --tags @billing,~@important

@billingFeature: Verify billing

@important Scenario: Missing product description

Scenario: Several products

HooksBefore do @browser = Browser.newend

After do @browser.closeend

HooksBefore do @browser = Browser.newend

After do @browser.closeend

Around do |scenario, block| Timeout.timeout(3) do block.call endend

HooksBefore do @browser = Browser.newend

After do @browser.closeend

Around do |scenario, block| Timeout.timeout(3) do block.call endend

Before('@foo')

After('~@bar')

Scenario Outline

Scenario Outline: eating Given there are <start> cucumbers When I eat <eat> cucumbers Then I should have <left> cucumbers

Examples: | start | eat | left | | 12 | 5 | 7 | | 20 | 5 | 15 |

So far, so good?

• Historias de Usuario

• Beneficio para el negocio

• Cucumber

So far, so good?

Coming next...• Testing de aceptación Web

• Beneficio para los devs

• Alternativas a Cucumber

• Historias de Usuario

• Beneficio para el negocio

• Cucumber

Aceptación para Web:Capybara

DSL de interacción web (navegador programático)

Usa el lenguaje del usuario

classmethod

GET / POSTparameters

Usa el lenguaje del usuario

classmethod

GET / POSTparameters

pageURLlink

form

visit "/wadus"

click_link "Add article"

click_link "Add article"click "Add article"

fill_in "Title", :with => "Wadus"

fill_in "Title", :with => "Wadus"choose "Option"

fill_in "Title", :with => "Wadus"choose "Option"check "Option"

fill_in "Title", :with => "Wadus"choose "Option"check "Option"uncheck "Option"

fill_in "Title", :with => "Wadus"choose "Option"check "Option"uncheck "Option"select "1980", :from => "Birth Year"

fill_in "Title", :with => "Wadus"choose "Option"check "Option"uncheck "Option"select "1980", :from => "Birth Year"click_button "Save"

fill_in "Title", :with => "Wadus"choose "Option"check "Option"uncheck "Option"select "1980", :from => "Birth Year"click_button "Save"click "Save"

within :css, ".article:first" do click_link "Edit"end

Matchers

page.should have_content("Wadus")

page.should have_content("Wadus")page.should_not have_content("Wadus")

page.should have_content("Wadus")page.should_not have_content("Wadus")

page.should have_css(".article", :text => "Wadus")

page.should have_content("Wadus")page.should_not have_content("Wadus")

page.should have_css(".article", :text => "Wadus")page.should have_css(".article", :count => 3)

page.should have_content("Wadus")page.should_not have_content("Wadus")

page.should have_css(".article", :text => "Wadus")page.should have_css(".article", :count => 3)

page.should have_xpath("//*[@class='article']")

page.should have_css(".article", :text => "Wadus") do |article| article.should have_css(".author", :text => "@porras") article.should have_css(".links") do |links| links.should have_css("a", :href => "http://wadus.info") links.should have_css("a", :href => "http://bit.ly/wadus") endend

save_and_open_page

Drivers

RackTest

RackTest

Basado en Rack

RackTest

FAST!

Basado en Rack

RackTest

FAST!Sin Javascript

Basado en Rack

RackTest

FAST!Sin Javascript

No es ‘real’

Basado en Rack

Selenium

Selenium

Basado en browsers programables

Selenium

Javascript

Basado en browsers programables

Selenium

JavascriptAceptación pura

Basado en browsers programables

Selenium

JavascriptAceptación pura

¡LENTO!

Basado en browsers programables

Selenium

JavascriptAceptación pura

¡LENTO!Un poco frágil

Basado en browsers programables

Selenium

JavascriptAceptación pura

¡LENTO!Un poco frágil

Java

Basado en browsers programables

Celerity / Culerity

Celerity / Culerity

Basado en HTMLUnit

Celerity / Culerity

Javascript

Basado en HTMLUnit

Celerity / Culerity

JavascriptAceptación casi pura

Basado en HTMLUnit

Celerity / Culerity

JavascriptAceptación casi pura

No muy lento

Basado en HTMLUnit

Celerity / Culerity

JavascriptAceptación casi pura

No muy lentoJava

Basado en HTMLUnit

Envjs

Envjs

Basado en SpiderMonkey

Envjs

Basado en SpiderMonkey

Javascript

Envjs

Basado en SpiderMonkey

JavascriptAceptación casi pura

Envjs

Basado en SpiderMonkey

JavascriptAceptación casi pura

No muy lento

Envjs

Basado en SpiderMonkey

JavascriptAceptación casi pura

No muy lentoRuby

Envjs

Basado en SpiderMonkey

JavascriptAceptación casi pura

No muy lentoRuby

Un poco verde

RackTest

Selenium

Culerity

0 37.5 75.0 112.5 150.0

Valor para el desarrollador

Excelente herramienta de verificación

Pero TDD no es QA

Pero TDD no es QAsólo

Dirige el desarrollo

“Outside-in testing is the best way to avoid overengineering”

http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/

Up-front

M

Up-front

C

M

Up-front

V

C

M

Up-front

V

C

M

T

Up-front

V

C

M

TT

Up-front

V

C

M

TTT

Up-front

Outside-in

F

Outside-in

F

V

Outside-in

F

V

C

Outside-in

F

V

CM

Outside-in

F

V

CM

F

V

CM

F

V

CM

F

V

CM

F

V

CM

Outside-in

“Tu interfaz es tu producto”

http://gettingreal.37signals.com/ch09_Interface_First.php

Permite otras prácticas ágiles

The Simplest Thing That Could Possibly Work

Dejar que el diseño “emerja”

Pair Programming

Refactorización contínua

Integración Contínua

Propiedad colectiva del código

Release Often

Continuous Deployment

Steak

A veces Cucumber (historias en texto plano)

no es la mejor opción

Todo el “valor para el desarrollador” sin la

burocracia extra

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

feature "Main page" do

background do create_user :login => "wadus" end

scenario "should show existing books" do create_book :title => "The Pragmatic Programmer"

login_as "wadus" visit "/"

page.should have_css(".book", :text => "The Pragmatic Programmer") end

end

Demo

http://vimeo.com/12468092

Más amigos

Spork

Spork

Sin spork

Con spork

0 3.25 6.50 9.75 13.00

Factorías

fixture_replacement

fixture_replacementfactory_girl

fixture_replacementfactory_girlmachinist

fixture_replacementfactory_girlmachinistfixjour

fixture_replacementfactory_girlmachinistfixjourcranky

fixture_replacementfactory_girlmachinistfixjourcranky

Ruby

module Factories def create_user(attrs = {}) attrs = attrs.dup attrs[:name] ||= String.random(10) attrs[:email] ||= "#{String.random(10)}@example.com" User.create!(attrs) end def create_article(attrs = {}) attrs = attrs.dup attrs[:title] ||= String.random(10) attrs[:author] ||= create_user Article.create!(attrs) end end

...

create_usercreate_user(:name => "Bartolo")create_articlecreate_article(:author => create_user(:name => "Mr. Wadus"))

Delorean

Delorean

Delorean

it "should show latest created user" do time_travel_to(3.minutes.ago) { create_user :name => "John" } time_travel_to(5.minutes.ago) { create_user :name => "Chris" }

get '/'

page.should have_content("John") page.should_not have_content("Chris")end

Database Cleaner

Database Cleaner

DatabaseCleaner.strategy = :truncationDatabaseCleaner.clean

Database Cleaner

DatabaseCleaner.strategy = :transactionDatabaseCleaner.clean

Email Spec

Email Spec

mailbox_for("porras@example.com").should have(1).email

open_email "porras@example.com"

current_email.should have_subject("Bienvenido a Wadus 2.0")current_email.should have_text("Hola, porras")

Webmock

Webmock

request(:post, "www.example.com"). with(:body => "abc"). should have_been_made.once

MundoPepino

“MundoPepino es un conjunto de pasos genéricos para testear una

aplicación Rails utilizando Cucumber”

Dado que tenemos un huertoY una huertaY un huerto "En el río"Y una huerta "En el castro"Y un huerto llamado "Regadío"Y una huerta llamada "Secano"Y 2 huertosY 2 huertos "Regadío"Y 2 huertos llamados "Secano"Y 2 huertas llamadas "Secano"Y 3 huertas llamadas "H-01, H-02 y H-03"Entonces tenemos en bbdd 17 huertosY tenemos en bbdd un huerto "En el río"Y tenemos en bbdd una huerta "En el castro"Y tenemos en bbdd 3 huertos "Regadío"Y tenemos en bbdd 5 huertas "Secano"Y tenemos en bbdd un huerto "H-01"Y tenemos en bbdd un huerto "H-02"Y tenemos en bbdd un huerto "H-03"

Cuando visito la página de creación de huertoEntonces debo ver la etiqueta H2 con el valor "Alta de Huerta"Cuando visito la página de alta de huertoEntonces debo ver la etiqueta H2 con el valor "Alta de Huerta"Cuando visito la página de nueva huertaEntonces debo ver la etiqueta H2 con el valor "Alta de Huerta"

Dado que visito la página de creación de huertoEntonces debo ver la etiqueta H2 con el valor "Alta de Huerta"

Dado que tenemos un huerto llamado "H-01"Y que dicho huerto tiene los siguientes aspersores: | nombre | caudal | unidad caudal || A-01 | 15 | m3 || A-02 | 12 | m3 || B-01 | 10 | m3 |Entonces tenemos en bbdd un huertoY tenemos en bbdd tres aspersoresY el huerto "H-01" tiene en bbdd un aspersor "A-01"Y el aspersor "A-01" tiene en bbdd como caudal "15"Y tiene en bbdd como unidad caudal "m3"Y el huerto "H-01" tiene en bbdd un aspersor "A-02"Y el aspersor "A-02" tiene en bbdd como caudal "12"Y tiene en bbdd como unidad caudal "m3"Y el huerto "H-01" tiene en bbdd un aspersor "B-01"Y el aspersor "B-01" tiene en bbdd como caudal "10"Y tiene en bbdd como unidad caudal "m3"

Recapitulando

• Aceptación Web

• Valor para los devs

• Steak (‘cause Cucumber

is for veggies)

• Otros amiguitos...

• Testing de aceptación Web

• Beneficio para los devs

• Alternativas a Cucumber

Gracias!

Gracias!

¿Preguntas?

Agile Spain 2010

@porras y @cavalle

top related