Cucumber
Best practices
Karoline Leite
Karoline Leite
➔ Qualidade e Testes
Desde 2008
➔ Engineering team leader
Creditas
➔ Founder & Organizer
Test Girls
BDDBOAS
PRÁTICAS
Cucumber
CucumberBDD
CucumberBDD
BDD is a collaborative
approach to software
development that bridges
the communication gap
between business and IT.
BDD helps teams communicate
requirements with more
precision, discover defects
early and produce software that
remains maintainable over time.
Boa prática
Automação de
testes é um
sub-produto do
BDD
O melhor drive é
prevenir
desperdício o
quanto antes
BDD não é sobre
teste de software
maintainable softwaresafe discovery
los three
amigos
discovery
workshops
specification
by example
Boa prática
Não se empolgue.
Respeite a pirâmide
de testes
CucumberBDD
Cucumber can be used to
implement automated
tests based on scenarios
described in your Gherkin
feature files.
Imperativo: Muitos detalhes, passo a passo
Declarativo: Conciso, direto ao ponto, poucos passos
Nível de abstração
Nível de abstração
Imperativo
Dado que eu abra o browser
E navegue para o site https://store.ministryoftesting.com
E clique no campo ‘Search’
Quando eu digitar ‘MUG’
E clicar em buscar
Então verei a página de busca mostrando os detalhes das canecas
Nível de abstração
Declarativo
Dado que eu esteja no e-commerce do Ministry of Testing
Quando eu fizer uma busca por caneca
Então verei a página de busca mostrando os detalhes das canecas
Nível de abstração
Imperativo vs Declarativo
Nível de abstração
Imperativo vs Declarativo
Boa prática
Menos é mais.
Menos passos,
menos
manutenção.
Detalhes da
implementação,
como IDs não
fazem sentido.
Foco deve ser no
comportamento.
Não tem certo ou
errado.
Experimente e
aprenda em
conjunto com seu
time.
Data Tables
Dado o valor dos itens que comprei
| 3 |
| 6 |
| 233 |
Quando a conta for somada
Então o total cobrado deve ser 238
Data Tables
Dado que eu esteja no e-commerce do Ministry of Testing
Quando eu fizer uma busca por caneca
Então verei a página de busca mostrando os detalhes das
canecas
| posicao | nome | preco |
| 1 | Bug Mug | £12.49 |
| 2 | Mug - White | £12.49 |
Então("verei a página de busca mostrando os detalhes das canecas") do |table|
data = table.raw
end
Data Tables
data => [["posicao", "nome", "preco"], ["1", "Bug Mug", "£12.49"],
["2", "Mug - White", "£12.49"]]
Esquema do Cenário
O cenário é o mesmo, mas os dados mudam!
São necessários inputs diferentes.
Esquema do Cenário
Cenário 1 - Somar
Dado que eu acesse a calculadora
Quando eu adicionar o valor 1
E apertar o botão de somar
E eu adicionar o valor 1
Então será exibido 2 como resultado
Esquema do Cenário
Cenário 2 - Subtrair
Dado que eu acesse a calculadora
Quando eu adicionar o valor 2
E apertar o botão de subtrair
E eu adicionar o valor 1
Então será exibido 1 como resultado
Esquema do Cenário
Cenário 3 - Multiplicar
Dado que eu acesse a calculadora
Quando eu adicionar o valor 4
E apertar o botão de multiplicar
E eu adicionar o valor 3
Então será exibido 12 como resultado
Esquema do Cenário
Cenário 4 - Dividir
Dado que eu acesse a calculadora
Quando eu adicionar o valor 4
E apertar o botão de dividir
E eu adicionar o valor 2
Então será exibido 2 como resultado
Esquema do Cenário
Dado que eu acesse a calculadora
Quando eu adicionar o valor <first_value>
E apertar o botão de <operation>
E eu adicionar o valor <second_value>
Então será exibido <result> como resultado
Exemplos:
| first_value | second_value| operation | result |
| 1 | 1 | sum | 2 |
| 2 | 1 | subtraction | 1 |
| 4 | 3 | multiplication| 12 |
| 4 | 2 | divide | 2 |
Boa prática
Menos é mais.
Você realmente
precisa testar
com tantos dados
assim?
Quaisquer tipo de
tabela é difícil de
ler. Use com
parcimônia.
Não tenha receio
de reescrever se
perceber que a
tabela está ficando
grande.
Tags
Tags are a great way to organise your features and
scenarios.
Referenciar para execução de um grupo de cenários
“Escopar” os hooks a grupos de cenarios
Tags
Referenciar para execução de um grupo de cenários
@calculator @fast @ready
Cenário: Fazer soma na calculadora web
Dado que eu acesse a calculadora
Quando eu adicionar o valor 1
E apertar o botão de somar
E eu adicionar o valor 1
Então será exibido 2 como resultado
Referenciar para execução de um grupo de cenários
$ cucumber features/ --tags @calculator
Tags
Tags
Referenciar para execução de um grupo de cenários
@calculator
@wip and not @slow
@smoke and @fast
@calculator or @spreadsheet
Tags são herdadas pelos elementos filhos.
Tags que são colocadas na Funcionalidade serão
herdadas pelos Cenários, Esquemas de Cenário e
Exemplos.
Tags que são colocadas no Esquema do Cenário serão
herdadas pelos Exemplos.
Tags
Herança de tags
@calculadora
Funcionalidade: Calculadora online
@website_search @gui
Cenário: Buscar caneca no site
Dado que eu esteja no e-commerce do Ministry of Testing
Quando eu fizer uma busca por caneca
Então verei a página de busca mostrando os detalhes das canecas
Tags
Herança de tags
Tags
“Escopar” os hooks a grupos de cenarios?
Boa prática
Crie um sistema
de tags relevante
e use bastante.
Crie um glossário
de tags e
explique-o no
README.md do
projeto.
Hooks
Hooks are blocks of code that can run at various points
in the Cucumber execution cycle.
Before
After
Around
After|BeforeStep
Hooks
Before do
# Do something before each scenario
end
Hooks
After do |scenario|
if scenario.failed?
# tire um printscreen
end
end
Hooks
Around('@fast') do |scenario, block|
Timeout.timeout(0.5) do
block.call
end
end
Boa prática
Separe arquivos
diferentes por
tipo de hook.
after_hooks.rb
before_hooks.rb
Não dê muita
responsabilidade
para os hooks. Eles
são invisíveis na
execução.
Nested Steps
Invocar um ou mais steps dentro de outro é um recurso
interessante para reaproveitar código.
Nested Steps
Dado("que a usuária {string} exista") do |user|
###
end
Dado("que eu faça login como {string}") do |user|
###
end
Nested Steps
Dado("que {string} esteja logada") do |user|
end
steps %{
que a usuária #{user} exista
que eu faça login como #{user}
}
Boa prática
Use com cuidado.
O acoplamento
fica muito forte
entre os steps.
Nested steps é
um ótimo recurso
para um baixo
nível de
reaproveitamento
e legibilidade.
Reports
$ cucumber --format html --out reports.html
Reports
Reports
Reports
After do |scenario|
if scenario.failed?
print_name = "#{scenario.name}.png"
Capybara.page.save_screenshot("reports/#{print_name}")
embed("reports/#{print_name}", "image/png","#{scenario.name}")
end
end
Boa prática
Os reports são
uma ótima forma
de compartilhar
os resultados dos
testes. Faça deles
seus aliados.
Retry
Se você tem um cenário flaky, o ideal é investigar a causa
e corrigir o mais rápido possível.
Mas quando não for possível, é necessário lidar com ele
sem impactar sua suíte toda.
Retry
$ cucumber feature --retry 2
4 scenarios (2 failed, 1 flaky, 1 passed)
Boa prática
Quanto mais
tentativas você
colocar, mais o
resultado do teste
vai demorar em
caso de falhas.
Configure seu CI
para avisar
quando flaky
cenários
ocorrerem.
World object
In Ruby, Cucumber runs scenarios in a World.
You can add any behaviour to the World, like helper
methods, or logging, etc.
World object
module CPFHelper
def cpf_generator
# implemente aqui
end
end
World(CPFHelper)
Boa prática
Organize os
helpers em
arquivos e
módulos com
responsabilidades
claras.
Usar helpers ajuda
a manter o
desacoplado,
organizado e
reutilizável.
Page Objects
class Home < SitePrism::Page
set_url '/index.htm'
set_url_matcher(/google.com/?/)
element :search_field, 'input[name="q"]'
element :search_button, 'button[name="btnK"]'
end
Page Objects
The public methods represent the services that the page offers
Try not to expose the internals of the page
Generally don't make assertions
Methods return other PageObjects
Need not represent an entire page
Different results for the same action are modelled as different methods
Boa prática
Não faça
assertions nas
nas suas
PageObjects. Use
expectations a
seu favor.
Não retorne
PageObjects nos
métodos. O
Cucumber não vai
bem com esse
acoplamento.
Page Objects
class Home < SitePrism::Page
element :search_field, 'input[name="q"]'
def result_verify
# implementação, retorna true ou false
end
end
expect(@home.result_verify).to be true
Page Objects
class Home < SitePrism::Page
element :search_field, 'input[name="q"]'
def result_verify
# implementação, retorna true ou false
end
end
Page Objects
expect(@home.element.text).to eq(expected_text)
Page Objects
class Home < SitePrism::Page
def search_item(name)
search_field.set(name)
search_button.click
return SearchResultPage.new
end
end
Page Objects
Dado("que eu faça uma busca por {string}") do |name|
page = Home.new
@next_page = page.search_item(name)
end
Page Objects
API Testing
Sua API também tem comportamento
É possível e faz sentido
Boa prática
É muito mais
tentador, mas
foque no
comportamento
da sua API, não na
implementação.
Organize os
payloads em
arquivos
separados dos
steps.
Crie seu API
objects para lidar
com cada
endpoint.
API Testing
Dado que eu me autentique na API do MoT
Quando eu fizer um post no endpoint “event/create”
Então o evento será criado
E o status retornado será 200
API Testing
Dado que eu me autentique na API do MoT
Quando eu enviar a criação de um novo evento
Então o evento será criado com sucesso
API Testing
Quando(“eu enviar a criação de um novo evento”) do
body = {
"id": "4",
"createdAt": "2019-03-18T08:14:59.675Z",
"name": "Handcrafted",
"local": "local 4"
}
end
API Testing
Quando(“eu enviar a criação de um novo evento”) do
…
url = ‘https://mot-sp/events/create’
end
API Testing
Quando(“eu enviar a criação de um novo evento”) do
…
...
response = HTTParty.post(url, :body => body)
end
API Testing
class MinistryOfTestingEventsAPI
include HTTParty
base_uri “https://mot-sp/events”
def create(body)
self.class.post(‘/create’, :body => body)
end
end
API Testing
→ create_body.json
{
"id": "4",
"createdAt": "2019-03-18T08:14:59.675Z",
"name": "Handcrafted",
"local": "local 4"
}
API Testing
Quando(“eu enviar a criação de um novo evento”) do
body = File.read(‘create_body.json’)
api = MinistryOfTestingEventsAPI.new
response = api.create(body)
end
Dicas de ouro
Experimente
novas ideias e
abordagens.
Refatore seu
código sempre.
Comece simples
e pratique.
Compartilhe seus
aprendizados e
dúvidas com seu
time e com sua
comunidade.
THANKS!
@karolxavierleite
Twitter
@karolineleite
Blog
http://blog.howtotest.com.br/

Cucumber Best Practices

  • 1.
  • 2.
    Karoline Leite ➔ Qualidadee Testes Desde 2008 ➔ Engineering team leader Creditas ➔ Founder & Organizer Test Girls
  • 3.
  • 4.
  • 5.
  • 6.
    BDD is acollaborative approach to software development that bridges the communication gap between business and IT.
  • 7.
    BDD helps teamscommunicate requirements with more precision, discover defects early and produce software that remains maintainable over time.
  • 8.
    Boa prática Automação de testesé um sub-produto do BDD O melhor drive é prevenir desperdício o quanto antes BDD não é sobre teste de software
  • 10.
    maintainable softwaresafe discovery losthree amigos discovery workshops specification by example
  • 11.
    Boa prática Não seempolgue. Respeite a pirâmide de testes
  • 12.
  • 13.
    Cucumber can beused to implement automated tests based on scenarios described in your Gherkin feature files.
  • 14.
    Imperativo: Muitos detalhes,passo a passo Declarativo: Conciso, direto ao ponto, poucos passos Nível de abstração
  • 15.
    Nível de abstração Imperativo Dadoque eu abra o browser E navegue para o site https://store.ministryoftesting.com E clique no campo ‘Search’ Quando eu digitar ‘MUG’ E clicar em buscar Então verei a página de busca mostrando os detalhes das canecas
  • 16.
    Nível de abstração Declarativo Dadoque eu esteja no e-commerce do Ministry of Testing Quando eu fizer uma busca por caneca Então verei a página de busca mostrando os detalhes das canecas
  • 17.
  • 18.
  • 19.
    Boa prática Menos émais. Menos passos, menos manutenção. Detalhes da implementação, como IDs não fazem sentido. Foco deve ser no comportamento. Não tem certo ou errado. Experimente e aprenda em conjunto com seu time.
  • 20.
    Data Tables Dado ovalor dos itens que comprei | 3 | | 6 | | 233 | Quando a conta for somada Então o total cobrado deve ser 238
  • 21.
    Data Tables Dado queeu esteja no e-commerce do Ministry of Testing Quando eu fizer uma busca por caneca Então verei a página de busca mostrando os detalhes das canecas | posicao | nome | preco | | 1 | Bug Mug | £12.49 | | 2 | Mug - White | £12.49 |
  • 22.
    Então("verei a páginade busca mostrando os detalhes das canecas") do |table| data = table.raw end Data Tables data => [["posicao", "nome", "preco"], ["1", "Bug Mug", "£12.49"], ["2", "Mug - White", "£12.49"]]
  • 23.
    Esquema do Cenário Ocenário é o mesmo, mas os dados mudam! São necessários inputs diferentes.
  • 24.
    Esquema do Cenário Cenário1 - Somar Dado que eu acesse a calculadora Quando eu adicionar o valor 1 E apertar o botão de somar E eu adicionar o valor 1 Então será exibido 2 como resultado
  • 25.
    Esquema do Cenário Cenário2 - Subtrair Dado que eu acesse a calculadora Quando eu adicionar o valor 2 E apertar o botão de subtrair E eu adicionar o valor 1 Então será exibido 1 como resultado
  • 26.
    Esquema do Cenário Cenário3 - Multiplicar Dado que eu acesse a calculadora Quando eu adicionar o valor 4 E apertar o botão de multiplicar E eu adicionar o valor 3 Então será exibido 12 como resultado
  • 27.
    Esquema do Cenário Cenário4 - Dividir Dado que eu acesse a calculadora Quando eu adicionar o valor 4 E apertar o botão de dividir E eu adicionar o valor 2 Então será exibido 2 como resultado
  • 28.
    Esquema do Cenário Dadoque eu acesse a calculadora Quando eu adicionar o valor <first_value> E apertar o botão de <operation> E eu adicionar o valor <second_value> Então será exibido <result> como resultado Exemplos: | first_value | second_value| operation | result | | 1 | 1 | sum | 2 | | 2 | 1 | subtraction | 1 | | 4 | 3 | multiplication| 12 | | 4 | 2 | divide | 2 |
  • 29.
    Boa prática Menos émais. Você realmente precisa testar com tantos dados assim? Quaisquer tipo de tabela é difícil de ler. Use com parcimônia. Não tenha receio de reescrever se perceber que a tabela está ficando grande.
  • 30.
    Tags Tags are agreat way to organise your features and scenarios. Referenciar para execução de um grupo de cenários “Escopar” os hooks a grupos de cenarios
  • 31.
    Tags Referenciar para execuçãode um grupo de cenários @calculator @fast @ready Cenário: Fazer soma na calculadora web Dado que eu acesse a calculadora Quando eu adicionar o valor 1 E apertar o botão de somar E eu adicionar o valor 1 Então será exibido 2 como resultado
  • 32.
    Referenciar para execuçãode um grupo de cenários $ cucumber features/ --tags @calculator Tags
  • 33.
    Tags Referenciar para execuçãode um grupo de cenários @calculator @wip and not @slow @smoke and @fast @calculator or @spreadsheet
  • 34.
    Tags são herdadaspelos elementos filhos. Tags que são colocadas na Funcionalidade serão herdadas pelos Cenários, Esquemas de Cenário e Exemplos. Tags que são colocadas no Esquema do Cenário serão herdadas pelos Exemplos. Tags Herança de tags
  • 35.
    @calculadora Funcionalidade: Calculadora online @website_search@gui Cenário: Buscar caneca no site Dado que eu esteja no e-commerce do Ministry of Testing Quando eu fizer uma busca por caneca Então verei a página de busca mostrando os detalhes das canecas Tags Herança de tags
  • 36.
    Tags “Escopar” os hooksa grupos de cenarios?
  • 37.
    Boa prática Crie umsistema de tags relevante e use bastante. Crie um glossário de tags e explique-o no README.md do projeto.
  • 38.
    Hooks Hooks are blocksof code that can run at various points in the Cucumber execution cycle. Before After Around After|BeforeStep
  • 39.
    Hooks Before do # Dosomething before each scenario end
  • 40.
    Hooks After do |scenario| ifscenario.failed? # tire um printscreen end end
  • 41.
    Hooks Around('@fast') do |scenario,block| Timeout.timeout(0.5) do block.call end end
  • 42.
    Boa prática Separe arquivos diferentespor tipo de hook. after_hooks.rb before_hooks.rb Não dê muita responsabilidade para os hooks. Eles são invisíveis na execução.
  • 43.
    Nested Steps Invocar umou mais steps dentro de outro é um recurso interessante para reaproveitar código.
  • 44.
    Nested Steps Dado("que ausuária {string} exista") do |user| ### end Dado("que eu faça login como {string}") do |user| ### end
  • 45.
    Nested Steps Dado("que {string}esteja logada") do |user| end steps %{ que a usuária #{user} exista que eu faça login como #{user} }
  • 46.
    Boa prática Use comcuidado. O acoplamento fica muito forte entre os steps. Nested steps é um ótimo recurso para um baixo nível de reaproveitamento e legibilidade.
  • 47.
    Reports $ cucumber --formathtml --out reports.html
  • 48.
  • 49.
  • 50.
    Reports After do |scenario| ifscenario.failed? print_name = "#{scenario.name}.png" Capybara.page.save_screenshot("reports/#{print_name}") embed("reports/#{print_name}", "image/png","#{scenario.name}") end end
  • 51.
    Boa prática Os reportssão uma ótima forma de compartilhar os resultados dos testes. Faça deles seus aliados.
  • 52.
    Retry Se você temum cenário flaky, o ideal é investigar a causa e corrigir o mais rápido possível. Mas quando não for possível, é necessário lidar com ele sem impactar sua suíte toda.
  • 53.
    Retry $ cucumber feature--retry 2 4 scenarios (2 failed, 1 flaky, 1 passed)
  • 54.
    Boa prática Quanto mais tentativasvocê colocar, mais o resultado do teste vai demorar em caso de falhas. Configure seu CI para avisar quando flaky cenários ocorrerem.
  • 55.
    World object In Ruby,Cucumber runs scenarios in a World. You can add any behaviour to the World, like helper methods, or logging, etc.
  • 56.
    World object module CPFHelper defcpf_generator # implemente aqui end end World(CPFHelper)
  • 57.
    Boa prática Organize os helpersem arquivos e módulos com responsabilidades claras. Usar helpers ajuda a manter o desacoplado, organizado e reutilizável.
  • 58.
    Page Objects class Home< SitePrism::Page set_url '/index.htm' set_url_matcher(/google.com/?/) element :search_field, 'input[name="q"]' element :search_button, 'button[name="btnK"]' end
  • 59.
    Page Objects The publicmethods represent the services that the page offers Try not to expose the internals of the page Generally don't make assertions Methods return other PageObjects Need not represent an entire page Different results for the same action are modelled as different methods
  • 60.
    Boa prática Não faça assertionsnas nas suas PageObjects. Use expectations a seu favor. Não retorne PageObjects nos métodos. O Cucumber não vai bem com esse acoplamento.
  • 61.
    Page Objects class Home< SitePrism::Page element :search_field, 'input[name="q"]' def result_verify # implementação, retorna true ou false end end
  • 62.
  • 63.
    class Home <SitePrism::Page element :search_field, 'input[name="q"]' def result_verify # implementação, retorna true ou false end end Page Objects
  • 64.
  • 65.
    class Home <SitePrism::Page def search_item(name) search_field.set(name) search_button.click return SearchResultPage.new end end Page Objects
  • 66.
    Dado("que eu façauma busca por {string}") do |name| page = Home.new @next_page = page.search_item(name) end Page Objects
  • 67.
    API Testing Sua APItambém tem comportamento É possível e faz sentido
  • 68.
    Boa prática É muitomais tentador, mas foque no comportamento da sua API, não na implementação. Organize os payloads em arquivos separados dos steps. Crie seu API objects para lidar com cada endpoint.
  • 69.
    API Testing Dado queeu me autentique na API do MoT Quando eu fizer um post no endpoint “event/create” Então o evento será criado E o status retornado será 200
  • 70.
    API Testing Dado queeu me autentique na API do MoT Quando eu enviar a criação de um novo evento Então o evento será criado com sucesso
  • 71.
    API Testing Quando(“eu enviara criação de um novo evento”) do body = { "id": "4", "createdAt": "2019-03-18T08:14:59.675Z", "name": "Handcrafted", "local": "local 4" } end
  • 72.
    API Testing Quando(“eu enviara criação de um novo evento”) do … url = ‘https://mot-sp/events/create’ end
  • 73.
    API Testing Quando(“eu enviara criação de um novo evento”) do … ... response = HTTParty.post(url, :body => body) end
  • 74.
    API Testing class MinistryOfTestingEventsAPI includeHTTParty base_uri “https://mot-sp/events” def create(body) self.class.post(‘/create’, :body => body) end end
  • 75.
    API Testing → create_body.json { "id":"4", "createdAt": "2019-03-18T08:14:59.675Z", "name": "Handcrafted", "local": "local 4" }
  • 76.
    API Testing Quando(“eu enviara criação de um novo evento”) do body = File.read(‘create_body.json’) api = MinistryOfTestingEventsAPI.new response = api.create(body) end
  • 77.
    Dicas de ouro Experimente novasideias e abordagens. Refatore seu código sempre. Comece simples e pratique. Compartilhe seus aprendizados e dúvidas com seu time e com sua comunidade.
  • 78.