Transformando os pepinos
           do cliente no código de
          testes da aplicação com
                  Cucumber

...
Sobre Urubatan

Trabalho com desenvolvimento desde 1997, já desenvolvi
  sistemas em diversas linguagens, como Delphi, C, ...
O cliente tem um problema a resolver
Descobrindo os problemas
Reuniões com o cliente        Cenários de uso do
                                sistema


Defini...
Behavior Driven Development
Cenário: Login

Scenario: Login of existent user
    Given I am on the login page
    When I provide valid credentials
   ...
Pensando melhor na feature
Feature Login
Feature: Login
  In order to make some money
  As the service provider
  I want existing users to be able to...
Tudo faz parte de um conjunto
Qual o ferramental completo?

•   Integração continua
•   Testes de aceitação automatizados
•   Relatório dos testes
•   D...
Ciclo de implementação
1.   Montar o backlog de features a serem implementadas
2.   Priorizar as features
3.   Pegar uma d...
Exemplo com Ruby on Rails
1.   Criar uma aplicação Rails
2.   Configurar o suporte ao cucumber
3.   Criar features
4.   Ex...
Criar uma aplicação Rails

rails new rails_sample
Configurar o ambiente e a aplicação
Instalar gems:
gem install rspec –version 2.0.0.beta.8
gem install rspec-rails –versio...
Feature Scaffold

rails generate cucumber:feature user name:string
  login:string password:string description:text
  email...
Geração espontânea de testes
Feature: Manage users
 In order to [goal]
 [stakeholder]
 wants [behaviour]

 Scenario: Register new user
  Given I am on ...
And I should see "email 1"


Scenario: Delete user
 Given the following users:
  |name|login|password|description|email|
 ...
Executar as features existentes

rake cucumber

Mostra quais testes passaram e qais falharam e
 porque.
cucumber –f pretty
Implementar as features

rails generate scaffold user name:string login:string
  password:string description:text email:st...
Cadastro Automágico
Executar as features existentes

rake cucumber

Mostra quais testes passaram e qais falharam e
 porque.
cucumber –f pretty
De volta ao Login
Pedindo ajuda ao Cucumber
Cucumber Ruby Back-End
When /^I provide valid credentials$/ do
 fill_in('login', :with => 'admin')
 fill_in('password', :w...
Implementando a tela de login

rails generate controller session new
Editando o arquivo routes.rb
 resource :sessions, :co...
Editando a view (new.html.erb)

<%= form_tag :action => :create do %>
 <label for="login">Login:</label><input
  type="tex...
O controller (session_controller.rb)
class SessionController < ApplicationController
 def new
 end

 def create
  login = ...
Resultado
Dados de exemplo
Feature: Login
                                                                    def path_to(page_name)...
Tudo Pronto!
Exemplo Web com Java
1. Criar um projeto Web Dinâmico com eclipse (ou
   outra IDE Java)
2. Copiar a pasta features do pro...
Automação do browser

require 'capybara'
require 'capybara/dsl'
include Capybara
Capybara.current_driver = :selenium
Capyb...
Configurar o Cucumber
require 'rspec'
require 'capybara/cucumber'
require 'capybara/session'                    # "before ...
Configurar os caminhos
Editar o arquivo paths.rb para que fique assim:
module NavigationHelpers
 def path_to(page_name)
  ...
Setup do Banco de dados
package commandLine;


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql....
Servidor HTTP Embedded
package commandLine;


import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Ser...
Alteração do login_steps.rb
require 'sqlite3‘
When /^I provide valid credentials$/ do
 fill_in('login', :with => 'admin')
...
Criando o servlet de login
package sample_servlets;                                           @Override
public class Login...
Criação da página de login
Criar o arquivo WEB-INF/jsps/login.jsp com o seguinte conteúdo:
<html>
<head>
<title>Sample Log...
Executar o cucumber

cucumber featureslogin.feature
Implementando o Gerenciamento de
            usuários
• Alterar os steps do cucumber para que insiram os
  dados no banco ...
Cucumber gerenciamento de usuários
Exemplo .NET
• Utilizando o              • Utilizaremos as mesmas
  VisualStudio.NET            features e cenários
  expr...
Criação do projeto

• Criar um novo projeto C# MVC 2 Blank
• Criar um controller de nome HomeController,
  uma View de nom...
Dados para o projeto
•   Criar um banco SQL Server de nome sample_db
•   Criar um Data Connection para este banco de dados...
Instalação de dependencias

gem install watir ruby-odbc
Configuração do cucumber
• Baixar exemplos do Watircuke de
  http://github.com/richdownie/watircuke
  • Baixar arquivos de...
Executando o Cucumber
SQL Server pelo Ruby

require 'rubygems„
require „odbc„
 @con = ODBC.connect('sample_db',nil,nil)
Começando a brincadeira

• Criar o arquivo
  step_definitions/cucumber_steps.rb
• Colocar todos os steps que o cucumber di...
Given /^there is an user with name "([^"]*)" and password "([^"]*)"$/ do |login, password|
 p = @con.proc("insert into use...
Criar o LoginController
DataClasses1DataContext dataClasses = new DataClasses1DataContext();
public ActionResult Index()
{...
Criar a view Login/Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ViewMasterPage1.Master"
    I...
Criando o Cadastro de Usuários

• Criar um UsersController com os métodos padrão
• Implementar os métodos da forma mais si...
Código para o Cucumber
When /^I fill in "([^"]*)" with "([^"]*)"$/ do |field,value|   end
 find_text_field field, value   ...
Executando o Cucumber
http://www.urubatan.com.br   rodrigo@urubatan.com.br
Referências

•   Meu livro - http://livro.urubatan.com.br
•   Meu blog - http://www.urubatan.com.br
•   Cucumber - http://...
Transformando os pepinos do cliente no código de testes da sua aplicação
Upcoming SlideShare
Loading in …5
×

Transformando os pepinos do cliente no código de testes da sua aplicação

3,912 views

Published on

Slides da palestra apresentada no AgileBrazil 2010 sobre BDD, Cucumber e testes de aplicações .NET, Java e Rails com o cucumber

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

No Downloads
Views
Total views
3,912
On SlideShare
0
From Embeds
0
Number of Embeds
835
Actions
Shares
0
Downloads
79
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Transformando os pepinos do cliente no código de testes da sua aplicação

  1. 1. Transformando os pepinos do cliente no código de testes da aplicação com Cucumber Rodrigo Urubatan http://www.urubatan.com.br rodrigo@urubatan.com.br
  2. 2. Sobre Urubatan Trabalho com desenvolvimento desde 1997, já desenvolvi sistemas em diversas linguagens, como Delphi, C, C++, PHP, ASP, ColdFusion, Assembly, Leather, Java e Ruby. Atualmente trabalho com pesquisa e desenvolvimento na HP, utilizando principalmente Java, e com Ruby em outros projetos e cursos. Alem de ser o autor do livro "Ruby On Rails: Desenvolvimento fácil e Rápido de aplicações web"
  3. 3. O cliente tem um problema a resolver
  4. 4. Descobrindo os problemas Reuniões com o cliente Cenários de uso do sistema Definição do Project Backlog Agile Business Analysis User Stories Lista do que deve ser feito
  5. 5. Behavior Driven Development
  6. 6. Cenário: Login Scenario: Login of existent user Given I am on the login page When I provide valid credentials And I press "Login" Then I should be redirected to "the home page"
  7. 7. Pensando melhor na feature
  8. 8. Feature Login Feature: Login In order to make some money As the service provider I want existing users to be able to access the system Scenario: Login of existent user Given I am on the login page When I provide valid credentials And I press "Login" Then I should be redirected to "the home page" Scenario: Login of inexistent user Given I am on the login page When I provide invalid credentials And I press "Login" Then I should be redirected to "the login page"
  9. 9. Tudo faz parte de um conjunto
  10. 10. Qual o ferramental completo? • Integração continua • Testes de aceitação automatizados • Relatório dos testes • Deploy automatico
  11. 11. Ciclo de implementação 1. Montar o backlog de features a serem implementadas 2. Priorizar as features 3. Pegar uma das features para implementar 4. Escrever os cenários/Testes de aceitação para a feature 5. Executar os cenários 6. Escrever código o suficiente para um cenário/teste passar 7. Executar os cenários novamente 8. Repetir passos 6 e 7 até que todos os cenários estejam passando
  12. 12. Exemplo com Ruby on Rails 1. Criar uma aplicação Rails 2. Configurar o suporte ao cucumber 3. Criar features 4. Executar os testes 5. Implementar as features 6. Executar os testes 7. Repetir passos 4 a 6 até que o sistema esteja pronto
  13. 13. Criar uma aplicação Rails rails new rails_sample
  14. 14. Configurar o ambiente e a aplicação Instalar gems: gem install rspec –version 2.0.0.beta.8 gem install rspec-rails –version 2.0.0.beta.8 gem install capybara database_cleaner cucumber-rails cucumber spork launchy Remover o arquivo “Gemfile” Executar: ruby script/rails generate cucumber:install --capybara --rspec
  15. 15. Feature Scaffold rails generate cucumber:feature user name:string login:string password:string description:text email:string
  16. 16. Geração espontânea de testes
  17. 17. Feature: Manage users In order to [goal] [stakeholder] wants [behaviour] Scenario: Register new user Given I am on the new user page When I fill in "Name" with "name 1" And I fill in "Login" with "login 1" And I fill in "Password" with "password 1" And I fill in "Description" with "description 1" And I fill in "Email" with "email 1" And I press "Create" Then I should see "name 1" And I should see "login 1" And I should see "password 1" And I should see "description 1"
  18. 18. And I should see "email 1" Scenario: Delete user Given the following users: |name|login|password|description|email| |name 1|login 1|password 1|description 1|email 1| |name 2|login 2|password 2|description 2|email 2| |name 3|login 3|password 3|description 3|email 3| |name 4|login 4|password 4|description 4|email 4| When I delete the 3rd user Then I should see the following users: |Name|Login|Password|Description|Email| |name 1|login 1|password 1|description 1|email 1| |name 2|login 2|password 2|description 2|email 2| |name 4|login 4|password 4|description 4|email 4|
  19. 19. Executar as features existentes rake cucumber Mostra quais testes passaram e qais falharam e porque.
  20. 20. cucumber –f pretty
  21. 21. Implementar as features rails generate scaffold user name:string login:string password:string description:text email:string
  22. 22. Cadastro Automágico
  23. 23. Executar as features existentes rake cucumber Mostra quais testes passaram e qais falharam e porque.
  24. 24. cucumber –f pretty
  25. 25. De volta ao Login
  26. 26. Pedindo ajuda ao Cucumber
  27. 27. Cucumber Ruby Back-End When /^I provide valid credentials$/ do fill_in('login', :with => 'admin') fill_in('password', :with => 'admin') end Then /^I should be redirected to "([^"]*)"$/ do |page_name| current_path = URI.parse(current_url).path current_path.should == path_to(page_name) end When /^I provide invalid credentials$/ do fill_in('login', :with => 'admin1') fill_in('password', :with => 'admin') end
  28. 28. Implementando a tela de login rails generate controller session new Editando o arquivo routes.rb resource :sessions, :controller => :session resources :users match 'login' => redirect('/sessions/new'), :as => :login root :to => „users#index‟
  29. 29. Editando a view (new.html.erb) <%= form_tag :action => :create do %> <label for="login">Login:</label><input type="text" id="login" name="login"/><br/> <label for="password">Password:</label><input type="password" id="password" name="password"/><br/> <input type="submit" value="Login"/> <% end %>
  30. 30. O controller (session_controller.rb) class SessionController < ApplicationController def new end def create login = params[:login] password = params[:password] u = User.where("login = :login and password = :password", :login => login, :password => password).first if u redirect_to root_path else redirect_to new_sessions_path end end end
  31. 31. Resultado
  32. 32. Dados de exemplo Feature: Login def path_to(page_name) In order to make some money As the service provider case page_name I want existing users to be able to access the system when /the homes?page/ Background: Given there is an user with name "admin" and password "admin" '/' when /the new user page/ Scenario: Login of existent user new_user_path Given I am on the login page When I provide valid credentials when /the new session page/ And I press "Login" new_sessions_path Then I should be redirected to "the home page" Scenario: Login of inexistent user Given I am on the login page Given /^there is an user with name "([^"]*)" and password When I provide invalid credentials "([^"]*)"$/ do |login, password| And I press "Login" User.create :login => login, :password => password Then I should be redirected to "the new session page" end
  33. 33. Tudo Pronto!
  34. 34. Exemplo Web com Java 1. Criar um projeto Web Dinâmico com eclipse (ou outra IDE Java) 2. Copiar a pasta features do projeto Rails 3. Configurar cucumber para testar aplicação Java 4. Executar cucumber 5. Implementar Login 6. Executar cucumber 7. Implementar cadastro de usuários 8. Executar cucumber
  35. 35. Automação do browser require 'capybara' require 'capybara/dsl' include Capybara Capybara.current_driver = :selenium Capybara.app_host = 'http://www.google.com' Capybara.run_server = false visit('/')
  36. 36. Configurar o Cucumber require 'rspec' require 'capybara/cucumber' require 'capybara/session' # "before all" require 'sqlite3' Before do require "selenium-webdriver" db = SQLite3::Database.new "sample_db.sqlite3" require 'cucumber/web/tableish' db.execute( "delete from users;" ) db.close Capybara.default_selector = :css Capybara.current_driver = :selenium Capybara.app_host = 'http://localhost:8080' end Capybara.run_server = false # "after all" After do Capybara.use_default_driver end
  37. 37. Configurar os caminhos Editar o arquivo paths.rb para que fique assim: module NavigationHelpers def path_to(page_name) case page_name when /the home page/ '/' when /the login page/ '/login' when /the new user page/ '/users/new' when /the users page/ '/users/' when /the new session page/ '/login' else raise "Can't find mapping from "#{page_name}" to a path.n" + "Now, go and add a mapping in #{__FILE__}" end end end World(NavigationHelpers)
  38. 38. Setup do Banco de dados package commandLine; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; public class DbSetup { public static void main(String[] args) throws ClassNotFoundException, SQLException { Class.forName("org.sqlite.JDBC"); Connection conn = DriverManager.getConnection("jdbc:sqlite:sample_db.sqlite3"); Statement stat = conn.createStatement(); stat.executeUpdate("drop table if exists users;"); stat.executeUpdate("create table users (name varchar(200), login varchar(200), password varchar(200), description text, email varchar(200));"); } }
  39. 39. Servidor HTTP Embedded package commandLine; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.webapp.WebAppContext; public class Main { public static void main(String[] args) throws Exception { Server server = new Server(8080); WebAppContext wac = new WebAppContext("WebContent", "/"); HandlerCollection handlers = new HandlerCollection(); Handler[] handlerArray = new Handler[] {wac, new DefaultHandler()}; handlers.setHandlers(handlerArray); server.setHandler(handlers); server.start(); } }
  40. 40. Alteração do login_steps.rb require 'sqlite3‘ When /^I provide valid credentials$/ do fill_in('login', :with => 'admin') fill_in('password', :with => 'admin') end Then /^I should be redirected to "([^"]*)"$/ do |page_name| current_path = URI.parse(current_url).path current_path.should == path_to(page_name) end When /^I provide invalid credentials$/ do fill_in('login', :with => 'admin1') fill_in('password', :with => 'admin') end Given /^there is an user with name "([^"]*)" and password "([^"]*)"$/ do |login, password| db = SQLite3::Database.new "sample_db.sqlite3" db.execute( "insert into users(login,password) values ( ?, ? )", login, password ) db.close end
  41. 41. Criando o servlet de login package sample_servlets; @Override public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { private static final long serialVersionUID = 1L; String login = req.getParameter("login"); String password = req.getParameter("password"); @Override Connection conn = null; protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { RequestDispatcher disp = req.getRequestDispatcher("WEB- Class.forName("org.sqlite.JDBC");conn = INF/jsps/login.jsp"); DriverManager.getConnection("jdbc:sqlite:sample_db.sqlite3"); disp.forward(req, resp); PreparedStatement pstmt = conn.prepareStatement("select * from users where login=? and password=?"); } pstmt.setString(1, login);pstmt.setString(2, password);ResultSet query = pstmt.executeQuery(); if (query.next()) {resp.sendRedirect("/");} else {resp.sendRedirect("/login");} } catch (Exception e) {e.printStackTrace();resp.sendRedirect("/login");} finally { if (conn != null) try {conn.close();} catch (SQLException e) {e.printStackTrace(); resp.sendRedirect("/login");} }}}
  42. 42. Criação da página de login Criar o arquivo WEB-INF/jsps/login.jsp com o seguinte conteúdo: <html> <head> <title>Sample Login Page</title> </head> <body> <form method="POST"><label for="login">Login</label><input type="text" id="login" name="login" /><br /> <label for="password">Password</label><input type="password" id="password" name="password" /><br /> <input type="submit" value="Login"/></form> </body> </html>
  43. 43. Executar o cucumber cucumber featureslogin.feature
  44. 44. Implementando o Gerenciamento de usuários • Alterar os steps do cucumber para que insiram os dados no banco correto • Exectar o cucumber • Criar um servlet UsersServlet • Criar as JSPs necessárias • Executar o cucumber
  45. 45. Cucumber gerenciamento de usuários
  46. 46. Exemplo .NET • Utilizando o • Utilizaremos as mesmas VisualStudio.NET features e cenários express • Primeiro • .NET MVC 2 implementaremos o • LINQ to SQL mapping Login • Cucumber + Watir • Depois o gerenciamento de usuários • SQL Server Express Data File
  47. 47. Criação do projeto • Criar um novo projeto C# MVC 2 Blank • Criar um controller de nome HomeController, uma View de nome Index para este controller e uma MasterPage • Dentro de Model criar um “New Item” “Link to SQL Classes” de nome DataClasses1
  48. 48. Dados para o projeto • Criar um banco SQL Server de nome sample_db • Criar um Data Connection para este banco de dados • Criar um DSN ODBC para o mesmo banco • Em Data Connections, na pasta Tables criar uma nova tabela de nome users com os campos • id, int, identity • name, nchar(200) • login, nchar(200) • password, nchar(200) • email, nchar(200) • description, ntext • Arrastar a tabela para o LINQ Designer e renomear para Users
  49. 49. Instalação de dependencias gem install watir ruby-odbc
  50. 50. Configuração do cucumber • Baixar exemplos do Watircuke de http://github.com/richdownie/watircuke • Baixar arquivos de features/support/ para o mesmo diretório no projeto • env.rb • paths.rb • watircuke.rb • Criar diretório features/step_definitions • Copiar os arquivos .feature do projeto de exemplo rails.
  51. 51. Executando o Cucumber
  52. 52. SQL Server pelo Ruby require 'rubygems„ require „odbc„ @con = ODBC.connect('sample_db',nil,nil)
  53. 53. Começando a brincadeira • Criar o arquivo step_definitions/cucumber_steps.rb • Colocar todos os steps que o cucumber disse estarem pending quando executado pelo console • Fazer o “merge” de todos os passos similares utilizando as expressões regulares
  54. 54. Given /^there is an user with name "([^"]*)" and password "([^"]*)"$/ do |login, password| p = @con.proc("insert into users(login, password) values (?, ?)") {} p.call(login, password) end Given /^I am on (.*)$/ do |page| @browser.goto path_to(page) end When /^I provide valid credentials$/ do find_text_field('login','admin') find_text_field('password','admin') end When /^I press "([^"]*)"$/ do |label| find_button label end Then /^I should be redirected to "([^"]*)"$/ do |page| raise "Not on the expected page" unless @browser.url == path_to(page) end When /^I provide invalid credentials$/ do find_text_field('login','admin2') find_text_field('password','admin2') end
  55. 55. Criar o LoginController DataClasses1DataContext dataClasses = new DataClasses1DataContext(); public ActionResult Index() { return View(); } public ActionResult Create(String login, String password) { try{ Users u = dataClasses.Users.First(usr => usr.login == login && usr.password == password); return Redirect("http://localhost:1467/"); } catch (Exception e) { return RedirectToAction("Index"); } }
  56. 56. Criar a view Login/Index.aspx <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ViewMasterPage1.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> System Login </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2><%: ViewData["Message"] %></h2> <form method="post" action="Login/Create"> <p> <label for="login">Login</label><input type="text" id="login" name="login"/> </p> <p> <label for="password">Password</label><input type="password" id="password“ ame="password"/> </p> <p><input type="submit" value="Login" /></p> </form> </asp:Content>
  57. 57. Criando o Cadastro de Usuários • Criar um UsersController com os métodos padrão • Implementar os métodos da forma mais simples possível • Implementar as views • Executar o cucumber novamente para testar a aplicação
  58. 58. Código para o Cucumber When /^I fill in "([^"]*)" with "([^"]*)"$/ do |field,value| end find_text_field field, value Then /^I should see the following users:$/ do |table| end tbl = @browser.table(:id, 'usersList').to_a Then /^I should see "([^"]*)"$/ do |value| idx = 1 raise "#{value} was not found in the document" unless table.hashes.each_with_index do |hash,index| @browser.text.include? value name = hash[:name] end login = hash[:login] Given /^the following users:$/ do |table| password = hash[:password] table.hashes.each do |hash| description = hash[:description] name = hash[:name] email = hash[:email] login = hash[:login] raise "unexpected users list" unless tbl[idx][2]==name password = hash[:password] raise "unexpected users list" unless tbl[idx][3]==login description = hash[:description] raise "unexpected users list" unless tbl[idx][4]==password email = hash[:email] raise "unexpected users list" unless tbl[idx][5]==description @con.run( "insert into users(name,login,password,description,email) values ( ?, raise "unexpected users list" unless tbl[idx][6]==email ?, ?, ?, ? )", name,login,password,description,email ) idx += 1 end end end end When /^I delete the 3rd user$/ do @browser.goto path_to('the users page') find_link('delete3')
  59. 59. Executando o Cucumber
  60. 60. http://www.urubatan.com.br rodrigo@urubatan.com.br
  61. 61. Referências • Meu livro - http://livro.urubatan.com.br • Meu blog - http://www.urubatan.com.br • Cucumber - http://wiki.github.com/aslakhellesoy/cucumber • Capybara- http://github.com/jnicklas/capybara • WebDriver - http://code.google.com/p/selenium/wiki/RubyBindings • Watir - http://watir.com/ • Watircuke - http://github.com/nofxx/watircuke • Rails – http://rubyonrails.org • ASP.NET MVC - http://www.asp.net/mvc

×