BDD de Aceptación con Ruby

2,611
-1

Published on

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

0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,611
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
41
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

BDD de Aceptación con Ruby

  1. 1. Pruebas BDD de Aceptación con Ruby Sergio Gil y Luismi Cavallé Agile Spain 2010
  2. 2. Historias de Usuario
  3. 3. [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/
  4. 4. 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
  5. 5. Anatomía de una historia Feature: Serve coffee In order to earn money Narrativa 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 Criterio de Aceptación
  6. 6. Narrativa Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times
  7. 7. Narrativa Título Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times
  8. 8. Narrativa Feature: Serve coffee In order to earn money Customers should be able to Rol buy coffee at all times ¿Quién?
  9. 9. Narrativa Feature: Serve coffee In order to earn money Customers should be able to buy coffee at all times Funcionalidad ¿Qué?
  10. 10. Narrativa Beneficio Feature: Serve coffee ¿Por qué? In order to earn money Customers should be able to buy coffee at all times
  11. 11. Regla de los 5 porqués
  12. 12. Regla de los 5 porqués ENSÉÑAME LA PASTA !!!!
  13. 13. 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
  14. 14. Criterio de Aceptación Contexto 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
  15. 15. Criterio de Aceptación Scenario: Buy last coffee Given there are 1 coffees left in the machine And I have deposited 1$ Eventos When I press the coffee button Then I should be served a coffee
  16. 16. 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 Resultados Then I should be served a coffee
  17. 17. 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 NE Then I should be served a coffee O D
  18. 18. Beneficio para el negocio Programadores Testers Analistas Clientes Managers
  19. 19. Beneficio para el negocio Programadores Testers Historia de Usuario Token de comunicación / discusión Priorización según beneficio / valor Criterio común de “terminado” Analistas Clientes Managers
  20. 20. Cucumber
  21. 21. 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
  22. 22. Pasos Given a user “john” with password “secret” When he tries to login as “james” Then he should see an error message
  23. 23. Definición de pasos Given a user “john” with password “secret”
  24. 24. Definición de pasos Given a user “john” with password “secret” Given /^a user "(.+)" with password "(.+)"$/ do |login, password|
  25. 25. Definición de pasos Given a user “john” with password “secret” Given /^a user "(.+)" with password "(.+)"$/ do |login, password| User.create! :login => login, :password => password end
  26. 26. Definición de pasos When he tries to login as “james”
  27. 27. Definición de pasos When he tries to login as “james” When /^he tries to login as "(.+)"$/ do |login|
  28. 28. Definición de pasos When he tries to login as “james” When /^he tries to login as "(.+)"$/ do |login| visit "/login" fill_in "Username", :with => login fill_in "Password", :with => "secret" click_button "Login" end
  29. 29. Definición de pasos Then he should see an error message
  30. 30. Definición de pasos Then he should see an error message Then /^I should see an error message$/ do
  31. 31. 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
  32. 32. Demo
  33. 33. http://vimeo.com/12441999
  34. 34. +40 Idiomas # language: es Caracterí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 |
  35. 35. 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); } } }
  36. 36. 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))))
  37. 37. Más funcionalidades...
  38. 38. 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
  39. 39. Tags @billing Feature: Verify billing @important Scenario: Missing product description Scenario: Several products $ cucumber --tags @billing,~@important
  40. 40. Hooks Before do @browser = Browser.new end After do @browser.close end
  41. 41. Hooks Before do @browser = Browser.new Around do |scenario, block| end Timeout.timeout(3) do block.call After do end @browser.close end end
  42. 42. Hooks Before do @browser = Browser.new Around do |scenario, block| end Timeout.timeout(3) do block.call After do end @browser.close end end Before('@foo') After('~@bar')
  43. 43. 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 |
  44. 44. So far, so good? • Historias de Usuario • Beneficio para el negocio • Cucumber
  45. 45. So far, so good? • Historias de Usuario • Beneficio para el negocio • Cucumber Coming next... • Testing de aceptación Web • Beneficio para los devs • Alternativas a Cucumber
  46. 46. Aceptación para Web: Capybara
  47. 47. DSL de interacción web (navegador programático)
  48. 48. Usa el lenguaje del usuario class method GET / POST parameters
  49. 49. Usa el lenguaje del usuario class page method URL GET / POST link parameters form
  50. 50. visit "/wadus"
  51. 51. click_link "Add article"
  52. 52. click_link "Add article" click "Add article"
  53. 53. fill_in "Title", :with => "Wadus"
  54. 54. fill_in "Title", :with => "Wadus" choose "Option"
  55. 55. fill_in "Title", :with => "Wadus" choose "Option" check "Option"
  56. 56. fill_in "Title", :with => "Wadus" choose "Option" check "Option" uncheck "Option"
  57. 57. fill_in "Title", :with => "Wadus" choose "Option" check "Option" uncheck "Option" select "1980", :from => "Birth Year"
  58. 58. fill_in "Title", :with => "Wadus" choose "Option" check "Option" uncheck "Option" select "1980", :from => "Birth Year" click_button "Save"
  59. 59. fill_in "Title", :with => "Wadus" choose "Option" check "Option" uncheck "Option" select "1980", :from => "Birth Year" click_button "Save" click "Save"
  60. 60. within :css, ".article:first" do click_link "Edit" end
  61. 61. Matchers
  62. 62. page.should have_content("Wadus")
  63. 63. page.should have_content("Wadus") page.should_not have_content("Wadus")
  64. 64. page.should have_content("Wadus") page.should_not have_content("Wadus") page.should have_css(".article", :text => "Wadus")
  65. 65. 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)
  66. 66. 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']")
  67. 67. 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") end end
  68. 68. save_and_open_page
  69. 69. Drivers
  70. 70. RackTest
  71. 71. RackTest Basado en Rack
  72. 72. RackTest Basado en Rack FAST!
  73. 73. RackTest Basado en Rack FAST! Sin Javascript
  74. 74. RackTest Basado en Rack FAST! Sin Javascript No es ‘real’
  75. 75. Selenium
  76. 76. Selenium Basado en browsers programables
  77. 77. Selenium Basado en browsers programables Javascript
  78. 78. Selenium Basado en browsers programables Javascript Aceptación pura
  79. 79. Selenium Basado en browsers programables Javascript Aceptación pura ¡LENTO!
  80. 80. Selenium Basado en browsers programables Javascript Aceptación pura ¡LENTO! Un poco frágil
  81. 81. Selenium Basado en browsers programables Javascript Aceptación pura ¡LENTO! Un poco frágil Java
  82. 82. Celerity / Culerity
  83. 83. Celerity / Culerity Basado en HTMLUnit
  84. 84. Celerity / Culerity Basado en HTMLUnit Javascript
  85. 85. Celerity / Culerity Basado en HTMLUnit Javascript Aceptación casi pura
  86. 86. Celerity / Culerity Basado en HTMLUnit Javascript Aceptación casi pura No muy lento
  87. 87. Celerity / Culerity Basado en HTMLUnit Javascript Aceptación casi pura No muy lento Java
  88. 88. Envjs
  89. 89. Envjs Basado en SpiderMonkey
  90. 90. Envjs Basado en SpiderMonkey Javascript
  91. 91. Envjs Basado en SpiderMonkey Javascript Aceptación casi pura
  92. 92. Envjs Basado en SpiderMonkey Javascript Aceptación casi pura No muy lento
  93. 93. Envjs Basado en SpiderMonkey Javascript Aceptación casi pura No muy lento Ruby
  94. 94. Envjs Basado en SpiderMonkey Javascript Aceptación casi pura No muy lento Ruby Un poco verde
  95. 95. RackTest Selenium Culerity 0 37.5 75.0 112.5 150.0
  96. 96. Valor para el desarrollador
  97. 97. Excelente herramienta de verificación
  98. 98. Pero TDD no es QA
  99. 99. Pero TDD no es QA sólo
  100. 100. Dirige el desarrollo
  101. 101. “Outside-in testing is the best way to avoid overengineering” http://www.sarahmei.com/blog/2010/05/29/outside-in-bdd/
  102. 102. Up-front
  103. 103. Up-front M
  104. 104. Up-front C M
  105. 105. Up-front V C M
  106. 106. Up-front T V C M
  107. 107. Up-front T T V C M
  108. 108. Up-front T T T V C M
  109. 109. Outside-in
  110. 110. Outside-in F
  111. 111. Outside-in F V
  112. 112. Outside-in F V C
  113. 113. Outside-in F V C M
  114. 114. Outside-in F F F V V V F F C C C V V C C MM M M M
  115. 115. “Tu interfaz es tu producto” http://gettingreal.37signals.com/ch09_Interface_First.php
  116. 116. Permite otras prácticas ágiles
  117. 117. The Simplest Thing That Could Possibly Work
  118. 118. Dejar que el diseño “emerja”
  119. 119. Pair Programming
  120. 120. Refactorización contínua
  121. 121. Integración Contínua
  122. 122. Propiedad colectiva del código
  123. 123. Release Often
  124. 124. Continuous Deployment
  125. 125. Steak
  126. 126. A veces Cucumber (historias en texto plano) no es la mejor opción
  127. 127. Todo el “valor para el desarrollador” sin la burocracia extra
  128. 128. 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
  129. 129. 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
  130. 130. 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
  131. 131. 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
  132. 132. 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
  133. 133. 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
  134. 134. 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
  135. 135. Demo
  136. 136. http://vimeo.com/12468092
  137. 137. Más amigos
  138. 138. Spork
  139. 139. Spork Sin spork Con spork 0 3.25 6.50 9.75 13.00
  140. 140. Factorías
  141. 141. fixture_replacement
  142. 142. fixture_replacement factory_girl
  143. 143. fixture_replacement factory_girl machinist
  144. 144. fixture_replacement factory_girl machinist fixjour
  145. 145. fixture_replacement factory_girl machinist fixjour cranky
  146. 146. fixture_replacement factory_girl machinist fixjour cranky Ruby
  147. 147. 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_user create_user(:name => "Bartolo") create_article create_article(:author => create_user(:name => "Mr. Wadus"))
  148. 148. Delorean
  149. 149. Delorean
  150. 150. 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
  151. 151. Database Cleaner
  152. 152. Database Cleaner DatabaseCleaner.strategy = :truncation DatabaseCleaner.clean
  153. 153. Database Cleaner DatabaseCleaner.strategy = :transaction DatabaseCleaner.clean
  154. 154. Email Spec
  155. 155. 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")
  156. 156. Webmock
  157. 157. Webmock request(:post, "www.example.com"). with(:body => "abc"). should have_been_made.once
  158. 158. MundoPepino
  159. 159. “MundoPepino es un conjunto de pasos genéricos para testear una aplicación Rails utilizando Cucumber”
  160. 160. Dado que tenemos un huerto Y una huerta Y 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 huertos Y 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 huertos Y 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"
  161. 161. Cuando visito la página de creación de huerto Entonces debo ver la etiqueta H2 con el valor "Alta de Huerta" Cuando visito la página de alta de huerto Entonces debo ver la etiqueta H2 con el valor "Alta de Huerta" Cuando visito la página de nueva huerta Entonces debo ver la etiqueta H2 con el valor "Alta de Huerta" Dado que visito la página de creación de huerto Entonces debo ver la etiqueta H2 con el valor "Alta de Huerta"
  162. 162. 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 huerto Y tenemos en bbdd tres aspersores Y 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"
  163. 163. Recapitulando • Aceptación Web • Valor para los devs • Steak (‘cause Cucumber is for veggies) • Otros amiguitos...
  164. 164. Gracias!
  165. 165. Gracias! @porras y @cavalle ¿Preg untas? Agile Spain 2010

×