SlideShare a Scribd company logo
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>
Transformando os pepinos do cliente no código de testes da sua aplicação
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

More Related Content

What's hot

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
Ignacio Martín
 
Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5Laravel 로 배우는 서버사이드 #5
Laravel 로 배우는 서버사이드 #5
성일 한
 
Meetup Performance
Meetup PerformanceMeetup Performance
Meetup Performance
Greg 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 More
Remy Sharp
 
MVS: An angular MVC
MVS: An angular MVCMVS: An angular MVC
MVS: An angular MVC
David Rodenas
 
jQuery Essentials
jQuery EssentialsjQuery Essentials
jQuery Essentials
Bedis ElAchèche
 
Rails 3 Beautiful Code
Rails 3 Beautiful CodeRails 3 Beautiful Code
Rails 3 Beautiful Code
GreggPollack
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
Jesús L. Domínguez Muriel
 
Deploying
DeployingDeploying
Deploying
soon
 
18.register login
18.register login18.register login
18.register login
Razvan Raducanu, PhD
 
Jquery ui
Jquery uiJquery ui
Jquery ui
adm_exoplatform
 
Unobtrusive javascript with jQuery
Unobtrusive javascript with jQueryUnobtrusive javascript with jQuery
Unobtrusive javascript with jQuery
Angel 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, FishBrain
Codemotion Tel Aviv
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
Yehuda Katz
 
Jquery
JqueryJquery
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
Yehuda 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 2017
Ryan Weaver
 
Drupal, meet Assetic
Drupal, meet AsseticDrupal, meet Assetic
Drupal, meet Assetic
Kris Wallsmith
 
jQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and BlingjQuery: Nuts, Bolts and Bling
jQuery: Nuts, Bolts and Bling
Doug Neiner
 

What's hot (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 to Transformando os pepinos do cliente no código de testes da sua aplicação

Introduce cucumber
Introduce cucumberIntroduce cucumber
Introduce cucumber
Bachue Zhou
 
Simple Web Apps With Sinatra
Simple Web Apps With SinatraSimple Web Apps With Sinatra
Simple Web Apps With Sinatra
a_l
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
Jonathan 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 System
Azharul Haque Shohan
 
Mojolicious
MojoliciousMojolicious
Mojolicious
Lenz Gschwendtner
 
浜松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} HTTP
Michael Francis
 
Behat - Drupal South 2018
Behat  - Drupal South 2018Behat  - Drupal South 2018
Behat - Drupal South 2018
Berend de Boer
 
Dancing with websocket
Dancing with websocketDancing with websocket
Dancing with websocket
Damien Krotkine
 
The Best (and Worst) of Django
The Best (and Worst) of DjangoThe Best (and Worst) of Django
The Best (and Worst) of Django
Jacob Kaplan-Moss
 
More to RoC weibo
More to RoC weiboMore to RoC weibo
More to RoC weibo
shaokun
 
20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev20130528 solution linux_frousseau_nopain_webdev
20130528 solution linux_frousseau_nopain_webdev
Frank Rousseau
 
Webauthn Tutorial
Webauthn TutorialWebauthn Tutorial
Webauthn Tutorial
FIDO Alliance
 
Tutorial asp.net
Tutorial  asp.netTutorial  asp.net
Tutorial asp.net
Vivek K. Singh
 
RSpec User Stories
RSpec User StoriesRSpec User Stories
RSpec User Stories
rahoulb
 
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 Dexy
ananelson
 
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
Katy Slemon
 
Mojolicious
MojoliciousMojolicious
Mojolicious
Marcos Rebelo
 
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
Ryan Weaver
 

Similar to 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
 

More from Rodrigo Urubatan

Ruby code smells
Ruby code smellsRuby code smells
Ruby code smells
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 framework
Rodrigo 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ída
Rodrigo 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 BDD
Rodrigo 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 bdd
Rodrigo Urubatan
 
vantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remotovantagens e desvantagens de trabalhar remoto
vantagens e desvantagens de trabalhar remoto
Rodrigo Urubatan
 
Using BDD to Solve communication problems
Using BDD to Solve communication problemsUsing BDD to Solve communication problems
Using BDD to Solve communication problems
Rodrigo 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.JS
Rodrigo 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 2015
Rodrigo 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.JS
Rodrigo 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 Text
Rodrigo Urubatan
 
Ruby para programadores java
Ruby para programadores javaRuby para programadores java
Ruby para programadores java
Rodrigo 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 HP
Rodrigo 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 cliente
Rodrigo Urubatan
 
Mini curso rails 3
Mini curso rails 3Mini curso rails 3
Mini curso rails 3
Rodrigo Urubatan
 

More from 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
 

Recently uploaded

leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
alexjohnson7307
 
How Social Media Hackers Help You to See Your Wife's Message.pdf
How Social Media Hackers Help You to See Your Wife's Message.pdfHow Social Media Hackers Help You to See Your Wife's Message.pdf
How Social Media Hackers Help You to See Your Wife's Message.pdf
HackersList
 
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
Kief Morris
 
EuroPython 2024 - Streamlining Testing in a Large Python Codebase
EuroPython 2024 - Streamlining Testing in a Large Python CodebaseEuroPython 2024 - Streamlining Testing in a Large Python Codebase
EuroPython 2024 - Streamlining Testing in a Large Python Codebase
Jimmy Lai
 
Salesforce AI & Einstein Copilot Workshop
Salesforce AI & Einstein Copilot WorkshopSalesforce AI & Einstein Copilot Workshop
Salesforce AI & Einstein Copilot Workshop
CEPTES Software Inc
 
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
maigasapphire
 
How RPA Help in the Transportation and Logistics Industry.pptx
How RPA Help in the Transportation and Logistics Industry.pptxHow RPA Help in the Transportation and Logistics Industry.pptx
How RPA Help in the Transportation and Logistics Industry.pptx
SynapseIndia
 
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptxRPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
SynapseIndia
 
WPRiders Company Presentation Slide Deck
WPRiders Company Presentation Slide DeckWPRiders Company Presentation Slide Deck
WPRiders Company Presentation Slide Deck
Lidia A.
 
WhatsApp Spy Online Trackers and Monitoring Apps
WhatsApp Spy Online Trackers and Monitoring AppsWhatsApp Spy Online Trackers and Monitoring Apps
WhatsApp Spy Online Trackers and Monitoring Apps
HackersList
 
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
aslasdfmkhan4750
 
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdfWhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
ArgaBisma
 
IPLOOK Remote-Sensing Satellite Solution
IPLOOK Remote-Sensing Satellite SolutionIPLOOK Remote-Sensing Satellite Solution
IPLOOK Remote-Sensing Satellite Solution
IPLOOK Networks
 
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSECHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
kumarjarun2010
 
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdfBT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
Neo4j
 
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes..."Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
Anant Gupta
 
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - MydbopsScaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
Mydbops
 
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-InTrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
TrustArc
 
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
digitalxplive
 
Data Integration Basics: Merging & Joining Data
Data Integration Basics: Merging & Joining DataData Integration Basics: Merging & Joining Data
Data Integration Basics: Merging & Joining Data
Safe Software
 

Recently uploaded (20)

leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
 
How Social Media Hackers Help You to See Your Wife's Message.pdf
How Social Media Hackers Help You to See Your Wife's Message.pdfHow Social Media Hackers Help You to See Your Wife's Message.pdf
How Social Media Hackers Help You to See Your Wife's Message.pdf
 
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
[Talk] Moving Beyond Spaghetti Infrastructure [AOTB] 2024-07-04.pdf
 
EuroPython 2024 - Streamlining Testing in a Large Python Codebase
EuroPython 2024 - Streamlining Testing in a Large Python CodebaseEuroPython 2024 - Streamlining Testing in a Large Python Codebase
EuroPython 2024 - Streamlining Testing in a Large Python Codebase
 
Salesforce AI & Einstein Copilot Workshop
Salesforce AI & Einstein Copilot WorkshopSalesforce AI & Einstein Copilot Workshop
Salesforce AI & Einstein Copilot Workshop
 
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
Girls Call Churchgate 9910780858 Provide Best And Top Girl Service And No1 in...
 
How RPA Help in the Transportation and Logistics Industry.pptx
How RPA Help in the Transportation and Logistics Industry.pptxHow RPA Help in the Transportation and Logistics Industry.pptx
How RPA Help in the Transportation and Logistics Industry.pptx
 
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptxRPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
RPA In Healthcare Benefits, Use Case, Trend And Challenges 2024.pptx
 
WPRiders Company Presentation Slide Deck
WPRiders Company Presentation Slide DeckWPRiders Company Presentation Slide Deck
WPRiders Company Presentation Slide Deck
 
WhatsApp Spy Online Trackers and Monitoring Apps
WhatsApp Spy Online Trackers and Monitoring AppsWhatsApp Spy Online Trackers and Monitoring Apps
WhatsApp Spy Online Trackers and Monitoring Apps
 
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
High Profile Girls Call ServiCe Hyderabad 0000000000 Tanisha Best High Class ...
 
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdfWhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
WhatsApp Image 2024-03-27 at 08.19.52_bfd93109.pdf
 
IPLOOK Remote-Sensing Satellite Solution
IPLOOK Remote-Sensing Satellite SolutionIPLOOK Remote-Sensing Satellite Solution
IPLOOK Remote-Sensing Satellite Solution
 
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSECHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
CHAPTER-8 COMPONENTS OF COMPUTER SYSTEM CLASS 9 CBSE
 
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdfBT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
BT & Neo4j: Knowledge Graphs for Critical Enterprise Systems.pptx.pdf
 
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes..."Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
"Mastering Graphic Design: Essential Tips and Tricks for Beginners and Profes...
 
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - MydbopsScaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
Scaling Connections in PostgreSQL Postgres Bangalore(PGBLR) Meetup-2 - Mydbops
 
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-InTrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
TrustArc Webinar - 2024 Data Privacy Trends: A Mid-Year Check-In
 
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
The Rise of AI in Cybersecurity How Machine Learning Will Shape Threat Detect...
 
Data Integration Basics: Merging & Joining Data
Data Integration Basics: Merging & Joining DataData Integration Basics: Merging & Joining Data
Data Integration Basics: Merging & Joining Data
 

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>
  • 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