SlideShare una empresa de Scribd logo
1 de 62
Descargar para leer sin conexión
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
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"
O cliente tem um problema a resolver
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
Behavior Driven Development
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"
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 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"
Tudo faz parte de um conjunto
Qual o ferramental completo?

•   Integração continua
•   Testes de aceitação automatizados
•   Relatório dos testes
•   Deploy automatico
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
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
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 –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
Feature Scaffold

rails generate cucumber:feature user name:string
  login:string password:string description:text
  email:string
Geração espontânea de testes
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"
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|
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:string
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', :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
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‟
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 %>
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
Resultado
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
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 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
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('/')
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
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)
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));");
     }
}
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();
}
}
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
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");}
                                                                   }}}
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>
Executar o cucumber

cucumber featureslogin.feature
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
Cucumber gerenciamento de usuários
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
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
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
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 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.
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 disse
  estarem pending quando executado pelo console
• Fazer o “merge” de todos os passos similares
  utilizando as expressões regulares
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
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");
    }
}
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>
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
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')
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://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

Más contenido relacionado

La actualidad más candente

Keeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackKeeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackIgnacio Martín
 
Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5성일 한
 
Meetup Performance
Meetup PerformanceMeetup Performance
Meetup PerformanceGreg Whalin
 
Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Kris Wallsmith
 
Write Less Do More
Write Less Do MoreWrite Less Do More
Write Less Do MoreRemy Sharp
 
Rails 3 Beautiful Code
Rails 3 Beautiful CodeRails 3 Beautiful Code
Rails 3 Beautiful CodeGreggPollack
 
Deploying
DeployingDeploying
Deployingsoon
 
Unobtrusive javascript with jQuery
Unobtrusive javascript with jQueryUnobtrusive javascript with jQuery
Unobtrusive javascript with jQueryAngel Ruiz
 
How to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrainHow to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrainCodemotion Tel Aviv
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017Ryan Weaver
 
jQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and BlingjQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and BlingDoug Neiner
 

La actualidad más candente (20)

Keeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and WebpackKeeping the frontend under control with Symfony and Webpack
Keeping the frontend under control with Symfony and Webpack
 
Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5
 
Meetup Performance
Meetup PerformanceMeetup Performance
Meetup Performance
 
Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)Assetic (Symfony Live Paris)
Assetic (Symfony Live Paris)
 
Write Less Do More
Write Less Do MoreWrite Less Do More
Write Less Do More
 
MVS: An angular MVC
MVS: An angular MVCMVS: An angular MVC
MVS: An angular MVC
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
 
Rails 3 Beautiful Code
Rails 3 Beautiful CodeRails 3 Beautiful Code
Rails 3 Beautiful Code
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
 
Deploying
DeployingDeploying
Deploying
 
18.register login
18.register login18.register login
18.register login
 
Jquery ui
Jquery uiJquery ui
Jquery ui
 
Unobtrusive javascript with jQuery
Unobtrusive javascript with jQueryUnobtrusive javascript with jQuery
Unobtrusive javascript with jQuery
 
How to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrainHow to actually use promises - Jakob Mattsson, FishBrain
How to actually use promises - Jakob Mattsson, FishBrain
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Jquery
JqueryJquery
Jquery
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
The Coolest Symfony Components you’ve never heard of - DrupalCon 2017
 
Drupal, meet Assetic
Drupal, meet AsseticDrupal, meet Assetic
Drupal, meet Assetic
 
jQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and BlingjQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and Bling
 

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

Introduce cucumber
Introduce cucumberIntroduce cucumber
Introduce cucumberBachue Zhou
 
Simple Web Apps With Sinatra
Simple Web Apps With SinatraSimple Web Apps With Sinatra
Simple Web Apps With Sinatraa_l
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For BeginnersJonathan Wage
 
Creating a Simple PHP and MySQL-Based Login System
Creating a Simple PHP and MySQL-Based Login SystemCreating a Simple PHP and MySQL-Based Login System
Creating a Simple PHP and MySQL-Based Login SystemAzharul Haque Shohan
 
浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編Masakuni Kato
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPMichael Francis
 
Behat - Drupal South 2018
Behat  - Drupal South 2018Behat  - Drupal South 2018
Behat - Drupal South 2018Berend de Boer
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of DjangoJacob Kaplan-Moss
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weiboshaokun
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdevFrank Rousseau
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Storiesrahoulb
 
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)Cogapp
 
Refresh Austin - Intro to Dexy
Refresh Austin - Intro to DexyRefresh Austin - Intro to Dexy
Refresh Austin - Intro to Dexyananelson
 
How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8Katy Slemon
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreRyan Weaver
 

Similar a Transformando os pepinos do cliente no código de testes da sua aplicação (20)

Introduce cucumber
Introduce cucumberIntroduce cucumber
Introduce cucumber
 
Simple Web Apps With Sinatra
Simple Web Apps With SinatraSimple Web Apps With Sinatra
Simple Web Apps With Sinatra
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
Creating a Simple PHP and MySQL-Based Login System
Creating a Simple PHP and MySQL-Based Login SystemCreating a Simple PHP and MySQL-Based Login System
Creating a Simple PHP and MySQL-Based Login System
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編浜松Rails3道場 其の参 Controller編
浜松Rails3道場 其の参 Controller編
 
Web::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTPWeb::Machine - Simpl{e,y} HTTP
Web::Machine - Simpl{e,y} HTTP
 
Behat - Drupal South 2018
Behat  - Drupal South 2018Behat  - Drupal South 2018
Behat - Drupal South 2018
 
Dancing with websocket
Dancing with websocketDancing with websocket
Dancing with websocket
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weibo
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
 
Webauthn Tutorial
Webauthn TutorialWebauthn Tutorial
Webauthn Tutorial
 
Tutorial asp.net
Tutorial  asp.netTutorial  asp.net
Tutorial asp.net
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Stories
 
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
Test-driven Development with Drupal and Codeception (DrupalCamp Brighton)
 
Refresh Austin - Intro to Dexy
Refresh Austin - Intro to DexyRefresh Austin - Intro to Dexy
Refresh Austin - Intro to Dexy
 
How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8How to implement multiple authentication guards in laravel 8
How to implement multiple authentication guards in laravel 8
 
Mojolicious
MojoliciousMojolicious
Mojolicious
 
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and moreSymfony Guard Authentication: Fun with API Token, Social Login, JWT and more
Symfony Guard Authentication: Fun with API Token, Social Login, JWT and more
 

Más de Rodrigo Urubatan

Data science in ruby is it possible? is it fast? should we use it?
Data science in ruby is it possible? is it fast? should we use it?Data science in ruby is it possible? is it fast? should we use it?
Data science in ruby is it possible? is it fast? should we use it?Rodrigo Urubatan
 
Data science in ruby, is it possible? is it fast? should we use it?
Data science in ruby, is it possible? is it fast? should we use it?Data science in ruby, is it possible? is it fast? should we use it?
Data science in ruby, is it possible? is it fast? should we use it?Rodrigo Urubatan
 
2018 the conf put git to work - increase the quality of your rails project...
2018 the conf   put git to work -  increase the quality of your rails project...2018 the conf   put git to work -  increase the quality of your rails project...
2018 the conf put git to work - increase the quality of your rails project...Rodrigo Urubatan
 
2018 RubyHACK: put git to work - increase the quality of your rails project...
2018 RubyHACK:  put git to work -  increase the quality of your rails project...2018 RubyHACK:  put git to work -  increase the quality of your rails project...
2018 RubyHACK: put git to work - increase the quality of your rails project...Rodrigo Urubatan
 
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...Rodrigo Urubatan
 
Your first game with unity3d framework
Your first game with unity3d frameworkYour first game with unity3d framework
Your first game with unity3d frameworkRodrigo Urubatan
 
Tdc Floripa 2017 - 8 falácias da programação distribuída
Tdc Floripa 2017 -  8 falácias da programação distribuídaTdc Floripa 2017 -  8 falácias da programação distribuída
Tdc Floripa 2017 - 8 falácias da programação distribuídaRodrigo Urubatan
 
Rubyconf2016 - Solving communication problems in distributed teams with BDD
Rubyconf2016 - Solving communication problems in distributed teams with BDDRubyconf2016 - Solving communication problems in distributed teams with BDD
Rubyconf2016 - Solving communication problems in distributed teams with BDDRodrigo Urubatan
 
resolvendo problemas de comunicação em equipes distribuídas com bdd
resolvendo problemas de comunicação em equipes distribuídas com bddresolvendo problemas de comunicação em equipes distribuídas com bdd
resolvendo problemas de comunicação em equipes distribuídas com bddRodrigo Urubatan
 
vantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remotovantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remotoRodrigo Urubatan
 
Using BDD to Solve communication problems
Using BDD to Solve communication problemsUsing BDD to Solve communication problems
Using BDD to Solve communication problemsRodrigo Urubatan
 
TDC2015 Porto Alegre - Interfaces ricas com Rails e React.JS
TDC2015  Porto Alegre - Interfaces ricas com Rails e React.JSTDC2015  Porto Alegre - Interfaces ricas com Rails e React.JS
TDC2015 Porto Alegre - Interfaces ricas com Rails e React.JSRodrigo Urubatan
 
Interfaces ricas com Rails e React.JS @ Rubyconf 2015
Interfaces ricas com Rails e React.JS @ Rubyconf 2015Interfaces ricas com Rails e React.JS @ Rubyconf 2015
Interfaces ricas com Rails e React.JS @ Rubyconf 2015Rodrigo Urubatan
 
TDC São Paulo 2015 - Interfaces Ricas com Rails e React.JS
TDC São Paulo 2015  - Interfaces Ricas com Rails e React.JSTDC São Paulo 2015  - Interfaces Ricas com Rails e React.JS
TDC São Paulo 2015 - Interfaces Ricas com Rails e React.JSRodrigo Urubatan
 
Full Text Search com Solr, MySQL Full text e PostgreSQL Full Text
Full Text Search com Solr, MySQL Full text e PostgreSQL Full TextFull Text Search com Solr, MySQL Full text e PostgreSQL Full Text
Full Text Search com Solr, MySQL Full text e PostgreSQL Full TextRodrigo Urubatan
 
Ruby para programadores java
Ruby para programadores javaRuby para programadores java
Ruby para programadores javaRodrigo Urubatan
 
Treinamento html5, css e java script apresentado na HP
Treinamento html5, css e java script apresentado na HPTreinamento html5, css e java script apresentado na HP
Treinamento html5, css e java script apresentado na HPRodrigo Urubatan
 
Ruby on rails impressione a você mesmo, seu chefe e seu cliente
Ruby on rails  impressione a você mesmo, seu chefe e seu clienteRuby on rails  impressione a você mesmo, seu chefe e seu cliente
Ruby on rails impressione a você mesmo, seu chefe e seu clienteRodrigo Urubatan
 

Más de Rodrigo Urubatan (20)

Ruby code smells
Ruby code smellsRuby code smells
Ruby code smells
 
Data science in ruby is it possible? is it fast? should we use it?
Data science in ruby is it possible? is it fast? should we use it?Data science in ruby is it possible? is it fast? should we use it?
Data science in ruby is it possible? is it fast? should we use it?
 
Data science in ruby, is it possible? is it fast? should we use it?
Data science in ruby, is it possible? is it fast? should we use it?Data science in ruby, is it possible? is it fast? should we use it?
Data science in ruby, is it possible? is it fast? should we use it?
 
2018 the conf put git to work - increase the quality of your rails project...
2018 the conf   put git to work -  increase the quality of your rails project...2018 the conf   put git to work -  increase the quality of your rails project...
2018 the conf put git to work - increase the quality of your rails project...
 
2018 RubyHACK: put git to work - increase the quality of your rails project...
2018 RubyHACK:  put git to work -  increase the quality of your rails project...2018 RubyHACK:  put git to work -  increase the quality of your rails project...
2018 RubyHACK: put git to work - increase the quality of your rails project...
 
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...
TDC2017 - POA - Aprendendo a usar Xamarin para desenvolver aplicações moveis ...
 
Your first game with unity3d framework
Your first game with unity3d frameworkYour first game with unity3d framework
Your first game with unity3d framework
 
Tdc Floripa 2017 - 8 falácias da programação distribuída
Tdc Floripa 2017 -  8 falácias da programação distribuídaTdc Floripa 2017 -  8 falácias da programação distribuída
Tdc Floripa 2017 - 8 falácias da programação distribuída
 
Rubyconf2016 - Solving communication problems in distributed teams with BDD
Rubyconf2016 - Solving communication problems in distributed teams with BDDRubyconf2016 - Solving communication problems in distributed teams with BDD
Rubyconf2016 - Solving communication problems in distributed teams with BDD
 
resolvendo problemas de comunicação em equipes distribuídas com bdd
resolvendo problemas de comunicação em equipes distribuídas com bddresolvendo problemas de comunicação em equipes distribuídas com bdd
resolvendo problemas de comunicação em equipes distribuídas com bdd
 
vantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remotovantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remoto
 
Using BDD to Solve communication problems
Using BDD to Solve communication problemsUsing BDD to Solve communication problems
Using BDD to Solve communication problems
 
TDC2015 Porto Alegre - Interfaces ricas com Rails e React.JS
TDC2015  Porto Alegre - Interfaces ricas com Rails e React.JSTDC2015  Porto Alegre - Interfaces ricas com Rails e React.JS
TDC2015 Porto Alegre - Interfaces ricas com Rails e React.JS
 
Interfaces ricas com Rails e React.JS @ Rubyconf 2015
Interfaces ricas com Rails e React.JS @ Rubyconf 2015Interfaces ricas com Rails e React.JS @ Rubyconf 2015
Interfaces ricas com Rails e React.JS @ Rubyconf 2015
 
TDC São Paulo 2015 - Interfaces Ricas com Rails e React.JS
TDC São Paulo 2015  - Interfaces Ricas com Rails e React.JSTDC São Paulo 2015  - Interfaces Ricas com Rails e React.JS
TDC São Paulo 2015 - Interfaces Ricas com Rails e React.JS
 
Full Text Search com Solr, MySQL Full text e PostgreSQL Full Text
Full Text Search com Solr, MySQL Full text e PostgreSQL Full TextFull Text Search com Solr, MySQL Full text e PostgreSQL Full Text
Full Text Search com Solr, MySQL Full text e PostgreSQL Full Text
 
Ruby para programadores java
Ruby para programadores javaRuby para programadores java
Ruby para programadores java
 
Treinamento html5, css e java script apresentado na HP
Treinamento html5, css e java script apresentado na HPTreinamento html5, css e java script apresentado na HP
Treinamento html5, css e java script apresentado na HP
 
Ruby on rails impressione a você mesmo, seu chefe e seu cliente
Ruby on rails  impressione a você mesmo, seu chefe e seu clienteRuby on rails  impressione a você mesmo, seu chefe e seu cliente
Ruby on rails impressione a você mesmo, seu chefe e seu cliente
 
Mini curso rails 3
Mini curso rails 3Mini curso rails 3
Mini curso rails 3
 

Último

My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfRankYa
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 

Último (20)

My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Search Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdfSearch Engine Optimization SEO PDF for 2024.pdf
Search Engine Optimization SEO PDF for 2024.pdf
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 

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

  • 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. 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. O cliente tem um problema a resolver
  • 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
  • 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"
  • 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. Tudo faz parte de um conjunto
  • 10. Qual o ferramental completo? • Integração continua • Testes de aceitação automatizados • Relatório dos testes • Deploy automatico
  • 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. 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. Criar uma aplicação Rails rails new rails_sample
  • 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. Feature Scaffold rails generate cucumber:feature user name:string login:string password:string description:text email:string
  • 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. 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. Executar as features existentes rake cucumber Mostra quais testes passaram e qais falharam e porque.
  • 21. Implementar as features rails generate scaffold user name:string login:string password:string description:text email:string
  • 23. Executar as features existentes rake cucumber Mostra quais testes passaram e qais falharam e porque.
  • 25. De volta ao Login
  • 26. Pedindo ajuda ao Cucumber
  • 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. 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. 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. 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
  • 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
  • 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. 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. 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. 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. 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. 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. 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. 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. 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. Executar o cucumber cucumber featureslogin.feature
  • 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
  • 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. 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. 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. Instalação de dependencias gem install watir ruby-odbc
  • 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.
  • 52. SQL Server pelo Ruby require 'rubygems„ require „odbc„ @con = ODBC.connect('sample_db',nil,nil)
  • 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. 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. 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. 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.
  • 58. 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
  • 59. 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')
  • 61. http://www.urubatan.com.br rodrigo@urubatan.com.br
  • 62. 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