Boas Práticas de Automação de Testes
Camilo Ribeiro

        Sr. QA Consultant na ThoughtWorks
        Developer e Devops nos fins de semana
        Blogger www.bugbang.com.br

        about.me/camiloribeiro
             @camiloribeiro
        github.com/camiloribeiro

        http://join.thoughtworks.com/profiles/camilo-ribeiro

Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
Carlos Palhares
     Development Consultant na
     ThoughtWorks

     Developer nos finais de semana

     cpalhares@thoughtworks.com

     xjunior.me
     twitter.com/xjunior
     github.com/xjunior


Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
~ 2.000Twers
                                                                           25 escritórios
                                                                             11 países
                                                                             1 cultura




http://social-impact.thoughtworks.com/


Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
Por que automatizar?
Record and Replay
require 'watir-webdriver'

browser = Watir::Browser.new :firefox
browser.goto "http://google.com.br"
browser.text_field(:name => 'q').set('Automação Rocks!')
browser.button(:name => 'btnG').click
sleep 1
if browser.text.include? 'The Bug Bang Theory 2.0'
puts 'Sucess'
else puts 'fail'
end
browser.close
require 'watir-webdriver'

browser = Watir::Browser.new :firefox
browser.goto "http://google.com.br"
browser.text_field(:name => 'q').set('Automação Rocks!')
browser.button(:name => 'btnG').click
sleep 1
if browser.text.include? 'The Bug Bang Theory 2.0'
puts 'Sucess'
else puts 'fail'
end
browser.close
Record and Replay
Bare Sleep
//pseudo-code
click_login_button();
fill_user(“Mario”);
//pseudo-code
click_login_button();
fill_user(“Mario”);
Erro: Campo “user” não encontrado
//pseudo-code
click_login_button();
sleep(3000)
fill_user(“Mario”);
Testes Lentos




Testes Irreais
Bare Sleep
Keep Pooling / Implicit Wait Time
//pseudo-code
  click_login_button();
  TIMEOUT.times do
       break if user_field.present?
       sleep 1
  end
  fill_user(“Mario”);



http://martinfowler.com/articles/nonDeterminism.html
require 'selenium-webdriver'

driver = Selenium::WebDriver.for :firefox
driver.manage.timeouts.implicit_wait = TIMEOUT

click_login_button
element = driver.find_element(:id => ”user")




http://seleniumhq.org/docs/04_webdriver_advanced.html
Lack of Isolation
Teste 1 – Verifica Soma dos produtos cadastrados:
   Produto A = 100,00
   Produto B = 50,00
   Soma produtos = 150,00

   Teste 2 – Cadastra um novo produto:
   Cadastra Produto C = 200,00
   Verifica se existe um produto chamado “Produto C”



http://martinfowler.com/articles/nonDeterminism.html
Ordem dos testes:

 Verificando soma dos produtos
 Cadastrando novo produto
Produtos
  Verificando soma dos produtos
    A soma dos produtos deve ser 150
  Cadastrando novo produto
    Devo cadastrar o produto C
Ordem dos testes:

 Cadastrando novo produto
 Verificando soma dos produtos
Produtos
  Cadastrando novo produto
    Devo cadastrar o produto C
  Verificando soma dos produtos
    A soma dos produtos deve ser 150

ExpectationNotMetError: expected: 4
Experado = 150,00
Resultado = 350,00
Testes isolados
Cenário: Cadastrar produtos
 Dado que eu estou na página de cadastro de produtos
 Quando eu cadastro o produto “C”
 E salvo
 Então eu devo ver o produto cadastrado

Cenário: Verificar soma dos produtos
 Dado que os produtos A e B estão cadastrados
 Quando eu solicito ver o valor total dos produtos
 Então eu devo ver o valor total como 150,00
Ordem dos testes:

 Verificando soma dos produtos
 Cadastrando novo produto
Cenário: Verificar soma dos produtos
 Dado que os produtos A e B estão cadastrados
 Quando eu solicito ver o valor total dos produtos
 Então eu devo ver o valor total como 150,00

Cenário: Cadastrar produtos
 Dado que eu estou na página de cadastro de produtos
 Quando eu cadastro o produto “C”
 E salvo
 Então eu devo ver o produto cadastrado
Ordem dos testes:

 Cadastrando novo produto
 Verificando soma dos produtos
Cenário: Cadastrar produtos
 Dado que eu estou na página de cadastro de produtos
 Quando eu cadastro o produto “C”
 E salvo
 Então eu devo ver o produto cadastrado

Cenário: Verificar soma dos produtos
 Dado que os produtos A e B estão cadastrados
 Quando eu solicito ver o valor total dos produtos
 Então eu devo ver o valor total como 150,00
Given When Then
Given I have an amazing chocolate (and I love it!)
When I eat the chocolate (Uhmm *.*)
Then there is no more chocolate :(
Clean up your mess
DRY – Don’t Repeat Yourself
(and don’t reinvent the wheel)
Data-Driven Testing
http://xunitpatterns.com/Data-Driven%20Test.html
var lado1 = 10
var lado2 = 10
var lado3 = 10

mathService.isTriangulo(lado1, lado2, lado3).should().beTruth()
mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘equilatero’)

var lado1 = 3
var lado2 = 4
var lado3 = 5

mathService.isTriangulo(lado1, lado2, lado3).should().beTruth()
mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘isóceles’)

var lado1 = 4
var lado2 = 4
var lado3 = 5

mathService.isTriangulo(lado1, lado2, lado3).should().beTruth()
mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘escaleno’)

…
var arquivo = File.read(“dados.csv”)

arquivo.each do
  var lado1 = arquivo[‘lado 1’]
  var lado2 = arquivo[‘lado 2’]
  var lado3 = arquivo[‘lado 3’]
  var tipoTriangulo = arquivo[‘tipo esperado’]

  mathService.isTriangulo(lado1, lado2, lado3).should().beTruth()

  var tipoRetornado = mathService.tipoTriangulo(lado1, lado2, lado3)
  tipoRetornado.should().be(tipoTriangulo)
end
Factory Pattern
http://blog.fossmo.net/post/What-is-the-factory-method-pattern.aspx
FactoryGirl.define do
 factory :candidate do
   name      'Candidato Fulano de Tal'
   short_name 'Fulano Legal'
   role    'Vereador'
end


 factory :candidate_without_role, :parent => :candidate do
   after(:build) do |candidate|
    candidate.role = nil
   end
 end




https://github.com/thoughtworks/voto-como-vamos
Separation Of Concerns
require 'watir-webdriver'

  browser = Watir::Browser.new :firefox
  browser.goto "http://google.com.br"
  browser.text_field(:name =>
  'q').set('Automação Rocks!')
  browser.button(:name => 'btnG').click
  sleep 1
  if browser.text.include? 'The Bug Bang Theory
  2.0'
  puts 'Sucess'
  else puts 'fail'
  end
  browser.close
http://www.bugbang.com.br/?p=2108
Mais vinte testes por favor 
require 'watir-webdriver'

                   browser = Watir::Browser.new :firefox
                   browser.goto "http://google.com.br"
                   browser.text_field(:name => 'q').set('Automação Rocks!')
                   browser.button(:name => 'btnG').click
                   if browser.text.include? 'The Bug Bang Theory 2.0'
                   puts 'Sucess'
                   else
                   puts 'fail'
                   end
                   browser.close

                   browser = Watir::Browser.new :firefox
                   browser.goto "http://google.com.br"
                   browser.text_field(:name => 'q').set('Selenium history')
                   browser.button(:name => 'btnG').click
                   if browser.text.include? 'ThoughtWorks'
                   puts 'Sucess'
                   else
                   puts 'fail'
                   end
                   browser.close

                   browser = Watir::Browser.new :firefox
                   browser.goto "http://google.com.br"
                   browser.text_field(:name => 'q').set('Desafio Selenium')
                   browser.button(:name => 'btnG').click
                   if browser.text.include? 'sembugs.blogspot.com.br'
                   puts 'Sucess'
                   else
                   puts 'fail'
                   end
                   browser.close

http://www.bugbang.com.br/?p=2108
Page-Object Model
Test Flow



Page 1   Page 2   …………   Page N
module Search
  class HomePage
    @url = “www.google.com”
    @search_field = “q”
    @search_button = “search”

      def input_search(value)
        @browser.text_field(:name => @search_field).set value
      end

      def run_search
        @browser.button(:class => @search_button).click
         return ResultPage.new
      end

      def visit
        @browser
      end
end
http://www.bugbang.com.br/?p=2108
module Search
  class HomePage        Elementos    da página como atributos
    @url = “www.google.com”
    @search_field = “q”
    @search_button = “search”

      def input_search(value)
        @browser.text_field(:name => @search_field).set value
      end

      def run_search
        @browser.button(:class => @search_button).click
         return ResultPage.new
      end

      def visit
        @browser
      end
end
http://www.bugbang.com.br/?p=2108
module Search
  class HomePage
    @url = “www.google.com”
    @search_field = “q”
    @search_button = “search”

      def input_search(value)
        @browser.text_field(:name => @search_field).set value
      end

      def run_search
        @browser.button(:class => @search_button).click
         return ResultPage.new
      end
                         Comportamentos como contrato
      def visit
        @browser
      end
end
http://www.bugbang.com.br/?p=2108
@page = Search.HomePage.new
 @page.visit
 @page.input_search(“xjunior.me”)
 @resultPage = @page.search
 @first = @resultPage.results.first
 @first.title.should eq(“Carlos Palhares”)




http://www.bugbang.com.br/?p=2108
Lendo e aprendendo 
Resumindo…


* Codigo de teste também é código;
     - (quase) todas as boas práticas se aplicam

* Don’t Repeat yourself!

* Código de teste deve ser uma represetação
automática da interação do usuário
     - Seu sistema pode não dar feedback suficiente
Dúvidas?
We are hiring!




         http://join.thoughtworks.com/
Muito Obrigado!!!




            Envie o seu Feedback e dúvidas para:
                cribeiro@thoughtworks.com
               cpalhares@thoughtworks.com


Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.

Boas práticas de Automação de Testes

  • 1.
    Boas Práticas deAutomação de Testes
  • 2.
    Camilo Ribeiro Sr. QA Consultant na ThoughtWorks Developer e Devops nos fins de semana Blogger www.bugbang.com.br about.me/camiloribeiro @camiloribeiro github.com/camiloribeiro http://join.thoughtworks.com/profiles/camilo-ribeiro Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
  • 3.
    Carlos Palhares Development Consultant na ThoughtWorks Developer nos finais de semana cpalhares@thoughtworks.com xjunior.me twitter.com/xjunior github.com/xjunior Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
  • 4.
    ~ 2.000Twers 25 escritórios 11 países 1 cultura http://social-impact.thoughtworks.com/ Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.
  • 5.
  • 8.
  • 10.
    require 'watir-webdriver' browser =Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Automação Rocks!') browser.button(:name => 'btnG').click sleep 1 if browser.text.include? 'The Bug Bang Theory 2.0' puts 'Sucess' else puts 'fail' end browser.close
  • 13.
    require 'watir-webdriver' browser =Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Automação Rocks!') browser.button(:name => 'btnG').click sleep 1 if browser.text.include? 'The Bug Bang Theory 2.0' puts 'Sucess' else puts 'fail' end browser.close
  • 14.
  • 15.
  • 19.
  • 21.
  • 24.
  • 27.
  • 28.
  • 29.
    Keep Pooling /Implicit Wait Time
  • 30.
    //pseudo-code click_login_button(); TIMEOUT.times do break if user_field.present? sleep 1 end fill_user(“Mario”); http://martinfowler.com/articles/nonDeterminism.html
  • 31.
    require 'selenium-webdriver' driver =Selenium::WebDriver.for :firefox driver.manage.timeouts.implicit_wait = TIMEOUT click_login_button element = driver.find_element(:id => ”user") http://seleniumhq.org/docs/04_webdriver_advanced.html
  • 33.
  • 34.
    Teste 1 –Verifica Soma dos produtos cadastrados: Produto A = 100,00 Produto B = 50,00 Soma produtos = 150,00 Teste 2 – Cadastra um novo produto: Cadastra Produto C = 200,00 Verifica se existe um produto chamado “Produto C” http://martinfowler.com/articles/nonDeterminism.html
  • 35.
    Ordem dos testes: Verificando soma dos produtos Cadastrando novo produto
  • 36.
    Produtos Verificandosoma dos produtos A soma dos produtos deve ser 150 Cadastrando novo produto Devo cadastrar o produto C
  • 37.
    Ordem dos testes: Cadastrando novo produto Verificando soma dos produtos
  • 39.
    Produtos Cadastrandonovo produto Devo cadastrar o produto C Verificando soma dos produtos A soma dos produtos deve ser 150 ExpectationNotMetError: expected: 4 Experado = 150,00 Resultado = 350,00
  • 40.
  • 41.
    Cenário: Cadastrar produtos Dado que eu estou na página de cadastro de produtos Quando eu cadastro o produto “C” E salvo Então eu devo ver o produto cadastrado Cenário: Verificar soma dos produtos Dado que os produtos A e B estão cadastrados Quando eu solicito ver o valor total dos produtos Então eu devo ver o valor total como 150,00
  • 42.
    Ordem dos testes: Verificando soma dos produtos Cadastrando novo produto
  • 43.
    Cenário: Verificar somados produtos Dado que os produtos A e B estão cadastrados Quando eu solicito ver o valor total dos produtos Então eu devo ver o valor total como 150,00 Cenário: Cadastrar produtos Dado que eu estou na página de cadastro de produtos Quando eu cadastro o produto “C” E salvo Então eu devo ver o produto cadastrado
  • 44.
    Ordem dos testes: Cadastrando novo produto Verificando soma dos produtos
  • 45.
    Cenário: Cadastrar produtos Dado que eu estou na página de cadastro de produtos Quando eu cadastro o produto “C” E salvo Então eu devo ver o produto cadastrado Cenário: Verificar soma dos produtos Dado que os produtos A e B estão cadastrados Quando eu solicito ver o valor total dos produtos Então eu devo ver o valor total como 150,00
  • 46.
  • 47.
    Given I havean amazing chocolate (and I love it!)
  • 48.
    When I eatthe chocolate (Uhmm *.*)
  • 49.
    Then there isno more chocolate :(
  • 50.
  • 52.
    DRY – Don’tRepeat Yourself (and don’t reinvent the wheel)
  • 53.
  • 54.
  • 55.
    var lado1 =10 var lado2 = 10 var lado3 = 10 mathService.isTriangulo(lado1, lado2, lado3).should().beTruth() mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘equilatero’) var lado1 = 3 var lado2 = 4 var lado3 = 5 mathService.isTriangulo(lado1, lado2, lado3).should().beTruth() mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘isóceles’) var lado1 = 4 var lado2 = 4 var lado3 = 5 mathService.isTriangulo(lado1, lado2, lado3).should().beTruth() mathService.tipoTriangulo(lado1, lado2, lado3).should().be(‘escaleno’) …
  • 56.
    var arquivo =File.read(“dados.csv”) arquivo.each do var lado1 = arquivo[‘lado 1’] var lado2 = arquivo[‘lado 2’] var lado3 = arquivo[‘lado 3’] var tipoTriangulo = arquivo[‘tipo esperado’] mathService.isTriangulo(lado1, lado2, lado3).should().beTruth() var tipoRetornado = mathService.tipoTriangulo(lado1, lado2, lado3) tipoRetornado.should().be(tipoTriangulo) end
  • 57.
  • 58.
  • 59.
    FactoryGirl.define do factory:candidate do name 'Candidato Fulano de Tal' short_name 'Fulano Legal' role 'Vereador' end factory :candidate_without_role, :parent => :candidate do after(:build) do |candidate| candidate.role = nil end end https://github.com/thoughtworks/voto-como-vamos
  • 60.
  • 62.
    require 'watir-webdriver' browser = Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Automação Rocks!') browser.button(:name => 'btnG').click sleep 1 if browser.text.include? 'The Bug Bang Theory 2.0' puts 'Sucess' else puts 'fail' end browser.close http://www.bugbang.com.br/?p=2108
  • 63.
    Mais vinte testespor favor 
  • 64.
    require 'watir-webdriver' browser = Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Automação Rocks!') browser.button(:name => 'btnG').click if browser.text.include? 'The Bug Bang Theory 2.0' puts 'Sucess' else puts 'fail' end browser.close browser = Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Selenium history') browser.button(:name => 'btnG').click if browser.text.include? 'ThoughtWorks' puts 'Sucess' else puts 'fail' end browser.close browser = Watir::Browser.new :firefox browser.goto "http://google.com.br" browser.text_field(:name => 'q').set('Desafio Selenium') browser.button(:name => 'btnG').click if browser.text.include? 'sembugs.blogspot.com.br' puts 'Sucess' else puts 'fail' end browser.close http://www.bugbang.com.br/?p=2108
  • 66.
  • 67.
    Test Flow Page 1 Page 2 ………… Page N
  • 68.
    module Search class HomePage @url = “www.google.com” @search_field = “q” @search_button = “search” def input_search(value) @browser.text_field(:name => @search_field).set value end def run_search @browser.button(:class => @search_button).click return ResultPage.new end def visit @browser end end http://www.bugbang.com.br/?p=2108
  • 69.
    module Search class HomePage Elementos da página como atributos @url = “www.google.com” @search_field = “q” @search_button = “search” def input_search(value) @browser.text_field(:name => @search_field).set value end def run_search @browser.button(:class => @search_button).click return ResultPage.new end def visit @browser end end http://www.bugbang.com.br/?p=2108
  • 70.
    module Search class HomePage @url = “www.google.com” @search_field = “q” @search_button = “search” def input_search(value) @browser.text_field(:name => @search_field).set value end def run_search @browser.button(:class => @search_button).click return ResultPage.new end Comportamentos como contrato def visit @browser end end http://www.bugbang.com.br/?p=2108
  • 71.
    @page = Search.HomePage.new @page.visit @page.input_search(“xjunior.me”) @resultPage = @page.search @first = @resultPage.results.first @first.title.should eq(“Carlos Palhares”) http://www.bugbang.com.br/?p=2108
  • 73.
  • 74.
    Resumindo… * Codigo deteste também é código; - (quase) todas as boas práticas se aplicam * Don’t Repeat yourself! * Código de teste deve ser uma represetação automática da interação do usuário - Seu sistema pode não dar feedback suficiente
  • 75.
  • 76.
    We are hiring! http://join.thoughtworks.com/
  • 77.
    Muito Obrigado!!! Envie o seu Feedback e dúvidas para: cribeiro@thoughtworks.com cpalhares@thoughtworks.com Private and Confidential. © 2011 ThoughtWorks, Ltd. All rights reserved.

Notas do Editor

  • #5 Camilo: Explica ThoughtWorks e seus projetos Carlos: Por que um Dev nessa talk?
  • #6 Camilo: - Processos tradicionais => Final do processo - Processos ágeis => a cada build => teste manual tomaria demasiado tempo (muito custo) => custo de documentar os testes - Teste manual => muita falha humana - Teste auto => menos falha humana Carlos - MOTO: AUTOMATE EVERYTHING - perder menos tempo (nenhum?) com tarefas repetitivas – não queremos as máquinas rindo da gente a noite
  • #7 Carlos: Boas práticas existem. Não são a solução para todos os problemas, não é pecado não usá-las.
  • #8 Carlos: Boas práticas existem. Não são a solução para todos os problemas, não é pecado não usá-las.
  • #9 Camilo
  • #10 Camilo
  • #11 Camilo
  • #12 Carlos - Código gerado pelo Selenium, deve ser bom, certo?
  • #13 Carlos
  • #14 Carlos
  • #15 Camilo
  • #16 Carlos: – sleep burro - Segurar nosso super performático teste
  • #17 Carlos: Teste manual
  • #18 Carlos
  • #19 Carlos
  • #20 Carlos: Reproduzir no código Rodou -> Passou -> Commit
  • #21 Carlos: CI => FAIL
  • #22 Camilo: Falhou por que? O Que o selenium faz?
  • #23 Camilo: O Que o selenium faz?
  • #24 Camilo: O Teste está mais rápido que a página? Carlos: Já sei, diminuir a velocidade do teste
  • #25 Carlos
  • #26 Carlos e Camilo
  • #27 Camilo: Red/Green tests
  • #28 Carlos: Adiciona mais tempo e soluciona para “ sempre ” Mesmo se sempre passar, vai ser lento
  • #29 Camilo: Não use o bare steep
  • #30 Camilo: Solução
  • #31 Camilo: Ação assíncrona Feedback da página Mais ação Carlos: O teste é o usuário, ambos precisam de feedback
  • #32 Camilo: Uma outra solução
  • #33 Camilo: Agora você está protegido
  • #34 Carlos
  • #35 Carlos: Explica Martin Fowler
  • #36 Carlos:
  • #37 Carlos:
  • #38 Carlos:
  • #39 Carlos:
  • #40 Carlos: Órdem importa Execução isolada passa
  • #41 Camilo
  • #42 Camilo
  • #43 Camilo
  • #44 Camilo
  • #45 Camilo
  • #46 Camilo
  • #47 Camilo: Given when then são os passos Não estamos falando de BDD
  • #48 Carlos: Prepara os dados do teste Nenhuma ação
  • #49 Carlos: Executa as ações Muda o estado
  • #50 Carlos: Verifica os resultados
  • #51 Carlos: Limpe sua bagunça Garante o isolamento Pode ser feito antes de cada teste (garantindo uma base sadia) Final de cada teste (mesmo se falhar) limpando a bagunça
  • #52 Carlos: - Um pouco mais perto de Fail Proof
  • #53 Camilo
  • #54 Camilo
  • #55 Camilo
  • #56 Camilo
  • #57 Camilo
  • #58 Carlos: - O mesmo padrão pode ser usado nos testes
  • #59 Carlos: Pra quem não conhece, essa é uma fábrica ….
  • #60 Camilo: Esse é o Voto Como Vamos uando um factory
  • #61 Carlos: O código de teste tem que ser bem feito Separação em camadas
  • #62 Camilo
  • #63 Camilo
  • #64 Camilo
  • #65 Camilo
  • #66 Carlos: que grande merda
  • #67 Carlos: - Page Object abstraction
  • #68 Carlos
  • #69 Carlos
  • #70 Carlos
  • #71 Carlos
  • #72 Carlos
  • #73 Camilo: Mais perto de fail proof
  • #74 Camilo: Livros legais
  • #75 Conversa entre os dois