Curso TDD Ruby on Rails #02: Test Driven Development

3,223 views

Published on

Lección 02 del curso de TDD en Ruby on Rails:
Desarrollo digirido por pruebas

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,223
On SlideShare
0
From Embeds
0
Number of Embeds
28
Actions
Shares
0
Downloads
0
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Curso TDD Ruby on Rails #02: Test Driven Development

  1. 1. CURSO DE TESTING OSL 12 – 16 DE ABRIL 2010 Cucumber y BDD Alberto Perdomo Web: http://albertoperdomo.net Email: alberto.perdomo@aentos.es Twitter: @albertoperdomo http://www.aentos.com
  2. 2. EL PROBLEMA DEL ENTENDIMIENTO → Los clientes hablan el lenguaje de su negocio → Los desarrolladores hablan lenguaje técnico → ¿Cómo escribir especifcaciones que podamos entender todos?
  3. 3. EL MOVIMIENTO BDD → BDD = Behaviour Driven Development → una nueva forma de enfocar el TDD → Origen: Dan North → http://blog.dannorth.net/introducing-bdd/ → Criterios de aceptación ejecutables
  4. 4. EL MOVIMIENTO DSL → DSL = Domain Specifc Languange → Lenguaje de programación enfocado a un determinado problema o dominio en particular → Negocios → Construcción → ...
  5. 5. CUCUMBER CUCUMBER = TESTING + BDD + DSL + Especifcaciones legibles → ¡Criterios de aceptación ejecutables!
  6. 6. CUCUMBER → Librería de tests de aceptación → ¡Historias de usuario! (en texto plano) como criterios de aceptación y test → Web en http://cukes.info/
  7. 7. CUCUMBER Extraído de http://cukes.info/
  8. 8. FEATURE: CARACTERÍSTICA BUSINESS VALUE Feature: Some terse yet descriptive text of what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Scenario: Some determinable business situation ¿Para qué sirve esta Given some precondition Funcionalidad? And some other precondition When some action by the actor And some other action ¿Qué valor aporta? And yet another action Then some testable outcome is achieved ¿A quién va dirigida? And something else we can check happens too Scenario: A different situation ... ¡HISTORIA DE USUARIO Y TEST EJECUTABLE! No tengas más de 4-6 escenarios por característica e intenta separar las características de forma que tengan sentido
  9. 9. SCENARIOS Una característica puede tener mútiples escenarios ESCENARIO Feature: Some terse yet descriptive text of what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Scenario: Some determinable business situation Caso de uso, Given some precondition Ejemplo And some other precondition When some action by the actor And some other action And yet another action Then some testable outcome is achieved And something else we can check happens too Scenario: A different situation ... GIVEN-WHEN-THEN steps ¡uno por línea!
  10. 10. GIVEN-WHEN-THEN Given → Precondición When → Acción Then → Resultado And / But → conectar steps ¡El orden no importa!
  11. 11. CUCUMBER: ELEMENTOS FEATURE features/login.feature CRITERIOS DE Given a user with email "admin@example.com" ACEPTACIÓN (TEXTO PLANO) Cucumber busca la defnición de los steps usando expresiones regulares features/step_defnitions/user_steps.rb STEP DEFINITIONS Given /^a user with email "(.*)"$/ do |email| user = Factory(:user, :email => email) (RUBY) end
  12. 12. CUCUMBER: STEPS → ¡Los steps son reusables! (si se escriben bien) → Utiliza variables → Utiliza parámetros o variables opcionales When /^I login as "(.*)"$/ do |login| visit login_path fill_in("Login", :with => login) fill_in("Password", :with => "secret") click_button("Login") end
  13. 13. CUCUMBER: PROBLEMA Pero... el cliente y yo hablamos español, no inglés...
  14. 14. CUCUMBER: IDIOMAS Cucumber soporta idiomas → ¡español! Feature → Característica Scenario → Escenario Given-When-Then → Dado-Cuando-Entonces And/But → Y/Pero … ...
  15. 15. CUCUMBER: EJEMPLO EN ESPAÑOL Hay que defnir el idioma en el que está escrito el feature #language: es Característica: Empresas gestión Para tener una relación de las empresas con las que trabajo Como usuario con rol admin Administro las empresas en el sistema Escenario: Registro de una nueva empresa Dado un usuario con rol "admin" "Fred" Cuando inicio sesión como "Fred" Y visito el listado de empresas Cuando pulso el enlace "Nueva empresa" Y registro una empresa Entonces debería ver "Empresa registrada correctamente" Y debería ver "Listado de empresas"
  16. 16. LIBRERÍAS Y STEPS GENÉRICOS → Librerías para probar aplicaciones Rails, Javascript y en general cualquier aplicación (web o no) → Rails → PHP, Java, .NET → una web cualquier, p.ej. Google → Steps predefnidos para lo más común: → Pulsar botones, seguir enlaces, rellenar campos, comprobar la presencia de textos, etc.
  17. 17. CUCUMBER FEATURES STEPS Libs. de tests para web Factorías, Otras libs. (emulan o usan un navegador) Mocks, ... en Ruby APLICACIÓN
  18. 18. LIBRERÍAS PARA WEB Capybara, Webrat, etc. → Emulando un navegador: más rápido, sin JS → Utilizando un navegador: más lento, con JS → Culerity (sin interfaz gráfco, ideal) → Selenium (Firefox) → Watir (Firefox, Safari, Chrome, IE) → ...
  19. 19. LA VELOCIDAD IMPORTA Para probar funcionalidades estándar → modo emulación → velocidad Para probar funcionalidades con AJAX o con efectos JS → modo navegador → más lento → culerity (relativamente rápido, no hay interfaz gráfca) → otros (más lento pero sirve para probar en navegadores específcos)
  20. 20. WEBRAT vs. CAPYBARA Webrat → compatible con muchos navegadores → se lanzan las pruebas en un modo espec., p.ej. Selenium → hay que lanzar las pruebas con/sin JS de forma separada Capybara → más reciente, confguración más sencilla → igual o más compatible → se puede cambiar la sesión en una misma ejecución de tests → API compatible con Webrat
  21. 21. CUCUMBER: VENTAJAS → Criterios de aceptación → Especifcación y test → en un mismo documento → en el lenguaje del cliente → en su idioma → vocabulario compartido → Evitamos confusiones al transformar especifcación en funcionalidades o tests unitarios → Ayudar a enfocarnos en las funcionalidades más valiosas → Documentación
  22. 22. MUCHO MÁS → Hooks → Tags → Tagged hooks → Multiline arguments → FIT tables → Background
  23. 23. CUCUMBER EN RAILS: ESTRUCTURA features/ → directorio de cucumber features/*.feature → características features/login.feature features/realizar_pedido.feature ... features/step_defnitions/*.rb → implementación de los steps features/step_defnitions/web_steps.rb → steps para visitar, clickar, etc. (de serie) features/step_defnitions/web_steps_es.rb → traducción al español (de serie) features/step_defnitions/pedido_steps.rb → steps relacionados con pedidos ... features/support/env.rb → conf. de cucumber features/support/paths.rb → traducción de rutas
  24. 24. STEPS WEB EN ESPAÑOL I Dado /^que estoy en (.+)$/ do |page_name| Given %{I am on #{page_name}} end Cuando /^voy a (.+)$/ do |page_name| When %{I go to #{page_name}} end Cuando /^pulso "([^"]*)"$/ do |button| When %{I press "#{button}"} end Cuando /^hago click en "([^"]*)"$/ do |link| When %{I follow "#{link}"} end Cuando /^completo "([^"]*)" con "([^"]*)"$/ do |field, value| When %{I fill in "#{field}" with "#{value}"} end ¡Vienen de serie!
  25. 25. STEPS WEB EN ESPAÑOL II Cuando /^selecciono "([^"]*)" de "([^"]*)"$/ do |value, field| When %{I select "#{value}" from "#{field}"} end Entonces /^debería ver "([^"]*)"$/ do |text| Then %{I should see "#{text}"} end Entonces /^no debería ver "([^"]*)"$/ do |text| Then %{I should not see "#{text}"} end Entonces /^debería estar en (.+)$/ do |page_name| Then %{I should be on #{page_name}} end Entonces /^múestrame la página$/ do Then %{show me the page} end y muchos más...
  26. 26. IMPLEMENTACIÓN DE STEPS Given /^(?:|I )am on (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end When /^(?:|I )press "([^"]*)"(?: within "([^"]*)")?$/ do |button, selector| with_scope(selector) do click_button(button) end end When /^(?:|I )follow "([^"]*)"(?: within "([^"]*)")?$/ do |link, selector| with_scope(selector) do click_link(link) end end When /^(?:|I )fill in "([^"]*)" with "([^"]*)"(?: within "([^"]*)")?$/ do |field, value, selector| with_scope(selector) do fill_in(field, :with => value) end end
  27. 27. EJEMPLO BDD: PASO 1 Defnimos el valor de negocio y un escenario features/sesion.feature #language: es Característica: Iniciar y cerrar sesión Para poder identificarme correctamente y realizar mis pedidos de forma segura Como usuario Quiero poder iniciar y cerrar sesión Escenario: Login correcto Dado un usuario con email "roger@test.com" Cuando voy a la portada Y inicio sesión como "roger@test.com" Entonces debería ver "Bienvenido"
  28. 28. EJEMPLO BDD: PASO 2 Ejecutamos el escenario $ cucumber features/sesion.feature VERDE: OK AMARILLO: NO DEFINIDO AZUL: SALTADO ROJO: ERROR Resumen Ayuda para Implementar los steps
  29. 29. EJEMPLO BDD: PASO 3 Implementar el primer step pendiente (si lo hay) features/step_defnitions/user_steps.rb Dado /^un usuario con email "([^"]*)"$/ do |email| Factory(:user, :email => email) end
  30. 30. EJEMPLO BDD: PASO 4 Volvemos a ejecutar el escenario Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica
  31. 31. EJEMPLO BDD: PASO 3 Implementar o arreglar el step features/step_defnitions/web_steps_es.rb features/support/paths.rb Cuando /^voy a (.+)$/ do |page_name| module NavigationHelpers When %{I go to #{page_name}} end def path_to(page_name) case page_name when /la portada/ '/' features/step_defnitions/web_steps.rb .... When /^(?:|I )go to (.+)$/ do |page_name| visit path_to(page_name) end Añadimos la ruta
  32. 32. EJEMPLO BDD: PASO 4 Volvemos a ejecutar el escenario Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica
  33. 33. EJEMPLO BDD: PASO 5 Implementar la funcionalidad para que el step pase confg/routes.rb map.root :controller => "user_sessions", :action => "new" # optional, this just sets the root route
  34. 34. EJEMPLO BDD: PASO 6 Volvemos a ejecutar el escenario Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica
  35. 35. EJEMPLO BDD: PASO 3 Implementar o arreglar el step features/step_defnitions/user_steps.rb Cuando /^inicio sesión con email "([^"]*)"$/ do |email| fill_in("Email", :with => email) fill_in("Contraseña", :with => "secret") click_button("Acceder") end
  36. 36. EJEMPLO BDD: PASO 4 Volvemos a ejecutar el escenario Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica
  37. 37. EJEMPLO BDD: PASO 5 Implementar la funcionalidad para que el step pase app/controllers/user_sessions_controller.rb class UserSessionsController < ApplicationController before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => :destroy def new @user_session = UserSession.new end app/views/user_sessions/new.html.erb <h1>Iniciar sesión</h1> <% form_for @user_session, :url => user_session_path do |f| %> <%= f.error_messages %> <%= f.label :email %><br /> <%= f.text_field :email %><br /> <br /> <%= f.label :password, "Contraseña" %><br /> <%= f.password_field :password %><br /> <br /> <%= f.check_box :remember_me, "No cerrar sesión" %><%= f.label :remember_me %><br /> <br /> <%= f.submit "Iniciar sesión" %> <% end %>
  38. 38. EJEMPLO BDD: PASO 6 Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica
  39. 39. EJEMPLO BDD: PASO 5 Implementar la funcionalidad para que el step pase app/controllers/user_sessions_controller.rb class UserSessionsController < ApplicationController before_filter :require_no_user, :only => [:new, :create] before_filter :require_user, :only => :destroy def new @user_session = UserSession.new end def create @user_session = UserSession.new(params[:user_session]) if @user_session.save flash[:notice] = "Bienvenido" redirect_back_or_default account_url else render :action => :new end end
  40. 40. EJEMPLO BDD: PASO 6 Ejecutar de nuevo Si hay errores → paso 5 Si hay steps pendientes → paso 3 Si está todo OK → siguiente escenario o característica TODO VERDE → HEMOS ACABADO
  41. 41. ¿PREGUNTAS?

×