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

Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsYoss Cohen
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...Karmanjay Verma
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...Wes McKinney
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfAarwolf Industries LLC
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Karmanjay Verma
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integrationmarketing932765
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkPixlogix Infotech
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 

Último (20)

Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Infrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platformsInfrared simulation and processing on Nvidia platforms
Infrared simulation and processing on Nvidia platforms
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...React JS; all concepts. Contains React Features, JSX, functional & Class comp...
React JS; all concepts. Contains React Features, JSX, functional & Class comp...
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
The Future Roadmap for the Composable Data Stack - Wes McKinney - Data Counci...
 
Landscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdfLandscape Catalogue 2024 Australia-1.pdf
Landscape Catalogue 2024 Australia-1.pdf
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#Microservices, Docker deploy and Microservices source code in C#
Microservices, Docker deploy and Microservices source code in C#
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS:  6 Ways to Automate Your Data IntegrationBridging Between CAD & GIS:  6 Ways to Automate Your Data Integration
Bridging Between CAD & GIS: 6 Ways to Automate Your Data Integration
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
React Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App FrameworkReact Native vs Ionic - The Best Mobile App Framework
React Native vs Ionic - The Best Mobile App Framework
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 

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