Test Automation Framework using Cucumber BDD overview (part 1)
BDD com Cucumber
-
Upload
eduardo-mendes-de-oliveira -
Category
Technology
-
view
1.579 -
download
1
description
Transcript of BDD com Cucumber
DesenvolvimentoBaseado em TestesBDD + CucumberEduardo [email protected]
@dudumendes
Introdução
Agenda
BDDCucumber
@dudumendes
BDD
TDDAbordagem de desenvolvimento de software em que os testes direcionam a implementação do software
TDD
Favorece o design de software
Expressa o comportamento do código
Documenta o código
@dudumendes
Testes unitários
Uma forma de aplicar o TDD
Traduzem as expectativas dos desenvolvedores sobre o comportamento do código
Fragilidade: Muito acoplado à implementação
Não traduz as expectativas dos usuários
Expectativas dos usuários
Casos de Uso
Estórias de Usuário
Ambas não possuem mecanismos de validação que traduzam as expectativas dos usuários
@dudumendes
BDDBehaviour Driven Development
@dudumendes
BDDBehaviour Driven Design
@dudumendes
BDD
“Behaviour-Driven Development is about implementing an application by describing its behaviour from the perspective of its stakeholders.”
David Chemlimsky
BDD
“Desenvolvimento orientado a comportamento diz respeito a implementar uma aplicação pela descrição do seu comportamento a partir da perspectiva de seus stakeholders.”
David Chemlimsky
Entender o mundo a partir da visao do STK_HD
Para entregar COISAS UTEISEntender o seu domínioSeus desafios e oportunidadesAs palavras que ele usa para descrever o comportamento que ele quer da aplicação
Mais que da visão de um stakeholderDo ponto de vista de qualquer que está envolvido no projeto
Behaviour Driven Development
Uma abordagem no estilo TDD
Documentação executável
Melhora a comunicação dos times
Esclarece os mal-entendimentos entre clientes, especialistas de domínio, desenvolvedores
Pode ser utilizada por todos os envolvidos no projeto
@dudumendes
BDDPrincípios
Enough is enough
Deliver stakeholder value
It’s all behaviour
@dudumendes
BDDPrincípios
O bastante é o bastanteTrabalhar para alcançar as expectativas dos stakeholders, mas evitar fazer mais do que se é necessário fazer
Entregar valor ao stakeholderSe você está fazendo algo que não entrega valor ou não aumenta sua habilidade de entrega de valor, pare e faça outra coisa
Tudo é sobre comportamentoAssim como podemos descrever o comportamento a partir da perspectivas dos stakeholders, também podemos descrever o comportamento de um código a partir de outro código que o utiliza
@dudumendes
Ciclo do BDD
red
red
greenrefactor
green
refactor
Cenário
Passo
@dudumendes
BDDO Ciclo
Stakeholder e analista discutem os requisitos
os requisitos são organizados em funcionalidades (features)
podem ser quebradas em estórias
fazem sentido para o stakeholder
@dudumendes
BDDO Ciclo
Stakeholder, analista e testador determinam os escopos das estórias
o analista pensa na funcionalidade de forma geral
o testador pensa em cenários concretos, com valores de entradas e saída
@dudumendes
BDDO Ciclo
Cenários prioritários são identificados
Stakeholder especifica exatamente o que quer entregue
Desenvolvedores implementam o bastante para satisfazer os cenários e nada mais
@dudumendes
BDDO Ciclo
Desenvolvedores
Automatizam cenários que orientam o desenvolvimento
Descrevem comportamentos esperados
Implementam os comportamentos
Refatoram
@dudumendes
Processo do BDD
@dudumendes
Estórias no BDD
BDDEstórias e Comportamento
Estórias
Correspondem às Estórias de Usuário
Expressam o comportamento das explicações em alto nível
Necessidade de frameworks de estórias
Cucumber, JBehave
BDDEstórias e Comportamento
Comportamento (ou Spec)
Correspondem às expectativas em nível de classeExpressam o comportamento a nível de serviço/componente
Estórias e Comportamento
Estórias de Usuário
Formado por um conjunto de Cenários (Scenarios)
critérios de aceitaçãoCada cenários possui
“entradas”, eventos e “resultados”Utilizados em projetos ágeis
Behaviour
Expressado como métodos de testesDefine o que aplicação deve e não deve fazer
Estrutura de uma estóriaConnextra Format
Título
Narrativa
As a [algum_papel]I want [alguma_necessidade]So that [beneficio/valor_da_caracteristica]
Critérios de aceitação (cenários)
Given [alguma(s)_condicao(oes)]When [evento_ocorrer]Then [certifico_algum_resultado]
Estrutura de uma estória
Título
Narrativa
Como um [algum_papel]Eu quero [alguma_necessidade]Para que [beneficio/valor_da_caracteristica]
Critérios de aceitação (cenários)
Dado [alguma(s)_condicao(oes)_entrada]Quando [evento_ocorrer]Então [certifico_algum_resultado]
Ciclo do BDD
red
red
grerefactor
grerefactor
Cenário
Passo
@dudumendes
Cucumber
Cucumber
Framework BDD Open Source baseado em RSpec
Criado por Aslak Hellesøy
Versão atual 1.2.1
CucumberCaracterísticas
Implementação em Ruby
Estórias de usuário baseadas em textos
Suporte a Injeção de dependências
Cucumberem 03 passos
Escrever uma estória e executar a estória (.feature)
obter os snippets com os passos do teste
Criar o arquivos de passos a partir dos snippets
Dar implementação aos passos
@dudumendes
Cucumber em 03 passosPasso 1 - Escrever uma estória
Estrutura de uma estóriaNarrativa
As a [algum_papel]
I want [alguma_necessidade]
So that [beneficio/valor_da_caracteristica]
Cenário
Given [alguma(s)_condicao(oes)]
When [evento_ocorrer]
Then [certifico_algum_resultado]
Estrutura de uma estóriaNarrative:
In order to [beneficio/valor_da_caracteristica]
As a [algum_papel]
I want to [alguma_necessidade]
Scenario: Nome do Cenário
Given [alguma(s)_condicao(oes)]
When [evento_ocorrer]
Then [certifico_algum_resultado]
@dudumendes
Estórias no Cucumberarquivos
de texto com extensão “.feature”
localização
diretório features
o comando procura por um diretório “features”
execução
comando cucumber
busca o diretório
Cucumber em 03 passosPasso 1 - Escrever uma estória
Feature: greeter says hello
In order to test Cucumber As a developer I want a greeter to say hello
Scenario: greeter says hello Given a greeter When I send it the greet message Then I should see "Hello Cucumber"
gretter_say_hello.feature
@dudumendes
cucumber
@dudumendes
continuação: snippets
@dudumendes
Resultado
O cucumber encontrou a feature
tentou executá-la
mas não sabe como executá-la
O cucumber sugeriu algumas dicas do código
@dudumendes
Cucumber em 03 passosPasso 2 - Criar o arquivo de passos
@dudumendes
Estórias no Cucumberarquivos
em Ruby com sufixo “_steps”
localização
features/step_definitions
execução
comando cucumber
@dudumendes
Given /^a greeter$/ do pending # express the regexp above with the code you wish you hadend
When /^I send it the greet message$/ do pending # express the regexp above with the code you wish you hadend
Then /^I should see "(.*?)"$/ do |arg1| pending # express the regexp above with the code you wish you hadend
greeter_steps.rb
@dudumendes
Cucumber em 03 passosPasso 3 - Dar implementação às estórias
@dudumendes
Given /^a greeter$/ do @greeter = CucumberGreeter.newend
When /^I send it the greet message$/ do @message = @greeter.greetend
Then /^I should see "(.*?)"$/ do |greeting| @message.should == greetingend
greeter_steps.rb
@dudumendes
@dudumendes
class CucumberGreeter def greet "Hello Cucumber" end end
Given /^a greeter$/ do @greeter = CucumberGreeter.newend
When /^I send it the greet message$/ do @message = @greeter.greetend
Then /^I should see "(.*?)"$/ do |greeting| expect(@message).to eql greetingend
@dudumendes
Cucumber Passos
Escrever uma estória
Arquivo de texto de extensão .feature
Recuperar os snippets
Criar o arquivo de passos
Estórias no Cucumber
Estórias em arquivos de textos
01 Narrativa (Narrative)
* Cenários (Scenarios)
Narrativa
Opcional
As a, In order to, I want to
Cenários no CucumberCenários consistem em:
Título
Passos:Given, When, Then
And
é possível colocar then antes do when
Cenários podem depender de outros
Comentários (!-- )
@dudumendes
Gherkin
Feature:
Scenario:
Given When Then
Internacionalização
Inglês é o padrão
# language: pt
Aplicação
Step definitions
Features
@dudumendes
Gherkin
Feature
Scenario
Scenario Outline
Scenarios
Given
When
Then
And
But
|
“”
#
@dudumendes
Gherkin --i18n
cucumber --i18n pt
@dudumendes
Exercício
Criar uma versão em português para o Hello World
Esquema de CenáriosScenario Outline
É comum em testes de aceitação definir-se exemplos de cenários reais com valores para verificar o estado de pronto da aplicação
Neste caso é possível se utilizar esquemas de cenários
Esquemas de cenários
Nos cenários os parâmetros devem ser envolvidos com sinais “<” e “>”
Eles devem ser declarados como esquemas de cenários
Após um cenário informam-se os valores válidos para os parâmetros em um tabela determinada por “Cenários:”
@dudumendes
# language: pt
Funcionalidade: Depositar Dinheiro
Esquema do Cenário: Depositar Dinheiro
Dado um cliente especial com saldo atual de <saldo_inicial> reais Quando ele realizar um deposito no valor <deposito> reais Então o deposito deve ser realizado E o saldo da conta atualizado para <saldo_final> reais Cenarios: valores possiveis | saldo_inicial | deposito | saldo_final | | 200 | 100 | 300 | | 200 | 100 | 300 | | 200 | 100 | 300 | | 200 | 100 | 300 |
@dudumendes
O comando cucumber
@dudumendes
Organizando featuresComando cucumber sem opções
serão procurados todos os arquivos .rb e .feature abaixo do diretório features
gera snippets para features indefinidas
cada Cenário e Passo tem comentários no final da linha
localização do cenário
nome do arquivo e número da linha
@dudumendes
# language: pt
Funcionalidade: Futuro hospede reserva um quarto
A fim de proporcionar mais comodidade Como dono do hotel Eu gostaria que os futuros hospedes reservassem quartos pela internet
Cenario: Reserva com sucesso Dado um hotel com "10" quartos e "0" reservas
@dudumendes
Organizando features
@dudumendes
Organizando featuresComando cucumber sem opções
serão procurados todos os arquivos .rb e .feature
Para projetos pequenos
caminho mais simples é organizar os arquivos dentro do diretório features
Para projetos maiores
crie subdiretorios para cada feature
vários arquivos em cada diretório com um subconjunto de ceários coesos
@dudumendes
Organizando featuresfeatures
seguro
medico
dentario
vida
previdencia
prbl
vgbl
cucumber features
executa todos
cucumber features/seguro
apenas os de seguro
cucumber features/seguro/medico
somente os médicos
@dudumendes
A vida de um cenárioNo processo de software
Cada cenário de uma funcionalidade precisa ser descrito e aprovado
Durente a especificação, os cenários podem estar
pendentes de aprovação
em progresso
estados mistos em uma mesma feature
pode ser necessário testar um subjconjunto da suites de testes
@dudumendes
Tags para features/scenarios
É como uma annotation
pode ser utilizada para marcar features e/ou scenarios
scenarios herdam as tags de suas features
podem ser utilizadas para
@dudumendes
Tags
@aprovada @sprint_1
Funcionalidade: paciente solicita consulta
@em_progresso
Cenario: paciente seleciona horario disponivel
cucumber --tags @em_progresso
@dudumendes
Tagscucumber --tags @cuc, @umber
cucumber --tags @cuc || @umber
executa todos os cenário com @cuc OU @umber
cucumber --tags @cuc --tags @umber
cucumber --tags @cuc && @umber
executa todos os cenário com @cuc E @umber
cucumber --tags ~@umber
cucumber --tags !@umber
executa todos os cenário sem @umber
@dudumendes
Passo-a-passo
@dudumendes
GivenWhen
Then
@dudumendesPassosUm passo candidato (StepCandidate) de uma feature deve estar associado a uma implementação de passo em um arquivo de passos
feature_X.feature
Cenario: Cenario A
Dado um contexto
Quando algo acontecer
Então Alguma coisa acontece
Arquivo de Passos
cenario_a_steps.rb
Dado um contexto
Quando algo acontecer
Então Alguma coisa acontece
implementação
implementação
implementação
Esta associação é feita através de expressões regulares
@dudumendes
Passos
Um passo candidato (StepCandidate) de uma feature deve estar associado a uma implementação de passo em um arquivo de passos
Caso esteja: passed
Caso contrário: undefined
@dudumendes
Definição de passosCriação do arquivo de feature
Quando você cria uma feature
executa o cucumber
cenários indefinidos
passos indefinidos
snippets de código
@dudumendes
ExecutaCenário
Lê 1.º passoExiste
definição?
Lê próximopasso
PassoIndefinido
Pendente?Exceção lançada?
Existe outro passo
CenárioFalho
Executa código de definição do
passo
Não
Sim
SimNão
CenárioPendente
Sim
CenárioIndefinido
Sim
Não
Não
CenárioPassou
@dudumendes
# language: pt
Funcionalidade: Futuro hospede reserva um quarto
A fim de proporcionar mais comodidade Como dono do hotel Eu gostaria que os futuros hospedes reservassem quartos pela internet
Cenario: Reservar com sucesso Dado um hotel com "10" quartos e "0" reservas
@dudumendes
Definição de passosCriação do arquivo de passos
Cria-se o arquivo de passos
Dado /^um hotel com "(.*?)" quartos e "(.*?)"
reservas$/ do |total_de_quartos, total_de_reservas|
end
@dudumendes
Given / When / ThenO formato Connextra
Utilizados para descrever os passos de um teste de aceitação
servem para descrever o cenário de uma vida real
expressam comportamento esperado
@dudumendes
GivenDado
Indica algo que se aceita como verdadeiro e certo em um cenário
Dado que tenho R$ 20 em minha conta
Dado que a Terra é redonda
Dado que hoje é sábado
É uma setença que descreve um contexto para os eventos e saídas que serão exercitados nos cenários
Não são pré-condições
@dudumendes
WhenQuando
Indica o evento que ocorre naquele cenário
Quando eu saco R$ 10
Quando eu pulo da estratosfera
Melhor que se tenha um evento por cenário
melhor descrição da intenção de cada cenário
falhas não serão ocultadas quando mais que um evento for descrito
@dudumendes
ThenEntão
Indica uma saída esperada
Então eu devo ter R$ 5 no final
Então eu devo pular antes de sair da atmosfera
Pode haver mais que uma saída por cenário
mas devem ser coesas
@dudumendes
And / ButE / Mas
Podem ser utilizados para complementar a descrição do Given e do Then
Cenario: Reserva com sucesso
Dado um hotel com "10" quartos E com "0" reservas Quando um futuro hospede reservar "1" quarto Entao reservas será "1" E o número dos quartos será "9"
@dudumendes
@dudumendes
Passos compostos
@dudumendes
Passos Compostos
Em BDD, o autor de cenários deve possuir a liberdade de aprofundar o foco de um determinado cenário
esta situação pode ultrapassar o limite de um cenário inicial
um passo pode depender de passos utilizados em outros cenários
Neste contexto, os cenários compostos são úteis
@dudumendes
Passos Compostos
Passos compostos permitem criar grupos de execução de passos ligados a um único passo, o que pode ser muito útil e poderoso
@dudumendes
Funcionalidade: Logar na aplicacao
Cenário: Usuario existe
Dado que o usuario "dudumendes" existe Dado que eu informei o login "dudumendes"
Dado /^que o usuario "(.*?)" existe$/ do |nome| # ...end
Dado /^que eu informei o login "(.*?)"$/ do |nome| # ...end
@dudumendes
Cenário: Usuario existe
Dado que o usuario "dudumendes" existe Dado que eu informei o login "dudumendes"
Dado que "dudumendes" está logado
Dado /^que o usuario "(.*?)" existe$/ do |nome| # ...end
Dado /^que eu informei o login "(.*?)"$/ do |nome| # ...end
Dado /^que (.*?) está logado$/ do |nome| step "que o usuario #{nome} existe" step "que eu informei o login #{nome}"end
@dudumendes
Funcionalidade: Transferir Dinheiro
Cenário: Transferir Dinheiro
Quando eu seleciono "conta corrente" como conta de origem E eu seleciono "poupanca" como conta de destino E eu informo que a quantidade é 20 E solicito executar
Quando /^eu seleciono "(.*?)" como conta de origem$/ do |origem|end
Quando /^eu seleciono "(.*?)" como conta de destino$/ do |destino|end
Quando /^eu informo que a quantidade é (\d+)$/ do |valor|end
Quando /^solicito executar$/ doend
@dudumendes
Funcionalidade: Transferir Dinheiro
Cenário: Transferir Dinheiro
Quando eu seleciono "conta corrente" como conta de origem E eu seleciono "poupanca" como conta de destino E eu informo que a quantidade é 20 E solicito executar Cenário: Transferir Dinheiro Resumido
Quando eu transfiro 20 da "conta corrente" para a "poupanca"
@dudumendes
Quando /^eu transfiro (\d+) da (.*?) para a (.*?)$/ do |valor, origem, destino| step "eu seleciono #{origem} como conta de origem" step "eu seleciono #{destino} como conta de destino" step "eu informo que a quantidade é #{valor}" step "solicito executar"end
@dudumendes
ExercícioCompletar a feature da Transferência e fazer passar
@dudumendes
Hooks
@dudumendes
Hooks
Before
Executado antes de cada cenário
After
Executado depois de cada cenário
AfterStep
Executado após cada passo
@dudumendes
Before do puts "Isto executa antes de cada cenário"end
After do puts "Isto executa depois de cada cenário"end
AfterStep do puts "Isto executa depois de cada passo"end
Before(“@cuc”) do puts "Isto executa antes de cada cenário com @cuc"end
@dudumendes
BackgroundContexto, Cenario de Fundo
Before, After, AfterStep
Não possuem descrição na feature
Background
Similar ao Before
Permite escrever passos na feature
@dudumendes
Funcionalidade: Convidar amigos
Contexto: Usuario logado
Dado que "dudumendes" esta logado Cenário: Convidar alguem que já é amigo Cenário: Convidar alguem que ainda não é amigo
@dudumendes
Tabelas em passos
@dudumendes
Funcionalidade: Baralho
Cenário: tres do mesmo tipo ganha de dois pares
Dado uma mao com as seguintes cartas | valor | naipe | | 2 | CORACAO | | 2 | ESPADAS | | 2 | PAUS | | 4 | OUROS | | A | CORACAO |
E outra mao com as seguintes cartas | valor | naipe | | 2 | CORACAO | | 2 | ESPADAS | | 4 | PAUS | | 4 | OUROS | | A | CORACAO | Entao a primeira mao deve ganhar da segunda mao
@dudumendes
Dado /^uma mao com as seguintes cartas$/ do |table| # table is a Cucumber::Ast::Table pending # express the regexp above with the code you wish you hadend
Dado /^outra mao com as seguintes cartas$/ do |table| # table is a Cucumber::Ast::Table pending # express the regexp above with the code you wish you hadend
Entao /^a primeira mao deve ganhar da segunda mao$/ do pending # express the regexp above with the code you wish you had
@dudumendes
Dado uma mao com as seguintes cartas | valor | naipe | | 2 | CORACAO | | 2 | ESPADAS | | 2 | PAUS | | 4 | OUROS | | A | CORACAO |
[ { :valor => '2', :naipe => 'CORACAO'}, { :valor => '2', :naipe => 'ESPADAS'}, { :valor => '2', :naipe => 'PAUS'}, { :valor => '4', :naipe => 'OUROS'}, { :valor => 'A', :naipe => 'CORACAO'}]
@dudumendes
Dado /^uma mao com as seguintes cartas$/ do |cartas| cartas.hashes.each {|hash| @primeira_mao << Carta.new(hash) }end
Dado /^outra mao com as seguintes cartas$/ do |cartas| cartas.hashes.each {|hash| @segunda_mao << Carta.new(hash) }end
@dudumendes
Exercíciofazer passar a feature
@dudumendes
Exercício
@dudumendes
Criar a feature e fazer passarDados os negociantes: |nome|rank||Larry|Estagio 3||Moe|Estagio 1||Curly|Estagio 2|Quando os negociantes são ordenados pelo nomeEntão os comerciantes devem vir na seguinte ordem:|nome|rank||Curly|Estagio 2||Larry|Estagio 3||Moe|Estagio 1|
@dudumendes
Rails + cucumber + rspecpequena dose
@dudumendes
rails new showtime
@dudumendes
Gemfilegroup :development, :test do
gem "rspec-rails"
gem "webrat"
end
group :test do
gem "cucumber-rails"
gem ”database_cleaner”
end
@dudumendes
bundle install
rails generate rspec:install (dependencias do rspec)
rails generate cucumber:install (dependencias do cucumber)
rake db:migrate
rake db:test:prepare
rake spec
rake cucumber
@dudumendes
Funcionalidade: Descrições Como um frequentador de cinema Eu quero ver horarios precisos e concisos Para que eu possa encontrar filmes que se encaixem no meu horario
Cenário: Exibir minutos para horarios que não terminam em 00 Dado um filme Quando eu configuro o horario para "2012-10-10" às "2:15pm" Então o horario deve ser "October 10, 2012 2:15pm"
/features/horario.feature
@dudumendes
snippet
@dudumendes
features/step_definitions/horario_steps.rb
# encoding: utf-8# language: pt
Dado /^um filme$/ do @filme = Filme.create!end
Quando /^eu configuro o horario para "(.*?)" às "(.*?)"$/ do |data, hora| @filme.update_attribute(:horario_data, Date.parse(data)) @filme.update_attribute(:horario_hora, hora)end
Então /^o horario deve ser "([^"]*)"$/ do |horario| expect(@filme.horario).to eql horarioend
@dudumendes
Criar o Model Filme
rails g model filme horario_data:date horario_hora:time
Além do modelo criará o spec spec/model/filme_spec.rb
rake db:migrate
rake db:test:prepare
@dudumendes
app/models/filme.rb
class Filme < ActiveRecord::Base attr_accessible :horario_data, :horario_hora def horario "#{data_formatada} #{hora_formatada}" end def data_formatada horario_data.strftime("%B %d, %Y") end def hora_formatada horario_hora.strftime("%l:%M%p").strip.downcase end end
@dudumendes
Validando o modelo
@dudumendes
rails generate model Email de:text para:text mensagem:text
@dudumendes
require 'spec_helper'
describe Email do pending "add some examples to (or delete) #{__FILE__}"end
@dudumendes
require 'spec_helper'
describe Email do context "validações:" do
it "para é obrigatório"
it "para é válido com email válido"
it "para é inválido com email inválido"
it "mensagem é obrigatória"
endend
@dudumendes
@dudumendesrequire 'spec_helper'
describe Email do context "validações:" do
it "para é obrigatório" do email = Email.create expect(email).to have(1).error_on(:para) end
it "para é válido com email válido"
it "para é inválido com email inválido"
it "mensagem é obrigatória"
endend
@dudumendes
@dudumendes
class Email < ActiveRecord::Base attr_accessible :mensagem, :paraend
app/models/email.rb
@dudumendes
class Email < ActiveRecord::Base attr_accessible :mensagem, :para validates_presence_of :paraend
app/models/email.rb
@dudumendes
# encoding: utf-8
require 'spec_helper'
describe Email do context "validações:" do it "de é obrigatório" do email = Email.create expect(email).to have(1).error_on(:de) end it "para é obrigatório" it "mensagem é obrigatória"
it "de deve ser um email" do email = Email.create(:de => "[email protected]") expect(email).to have(:no).error_on(:de) end it "para deve ser um email" endend
@dudumendes
@dudumendes
describe Email do context "validações:" do it "para é obrigatório" do email = Email.create expect(email).to have(1).error_on(:para) end it "para é válido com email válido" do email = Email.create(:para => "[email protected]") expect(email).to have(:no).error_on(:para) end it "para é inválido com email inválido" it "mensagem é obrigatória" endend
@dudumendes
@dudumendes
# encoding: utf-8
require 'spec_helper'
describe Email do context "validações:" do it "para é obrigatório" do email = Email.create expect(email).to have(1).error_on(:para) end it "para é válido com email válido" do email = Email.create(:para => "[email protected]") expect(email).to have(:no).error_on(:para) end it "para é inválido com email inválido" do email = Email.create(:para => "invalido") expect(email).to have(1).error_on(:para) end it "mensagem é obrigatória" endend
@dudumendes
class Email < ActiveRecord::Base attr_accessible :mensagem, :para validates_presence_of :para validates_format_of :para, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => trueend
@dudumendesdescribe Email do context "validações:" do it "para é obrigatório" do email = Email.create expect(email).to have(1).error_on(:para) end it "para é válido com email válido" do email = Email.create(:para => "[email protected]") expect(email).to have(:no).error_on(:para) end it "para é inválido com email inválido" do email = Email.create(:para => "invalido") expect(email).to have(1).error_on(:para) end it "mensagem é obrigatória" do email = Email.create expect(email).to have(1).error_on(:mensagem) end endend
@dudumendes
class Email < ActiveRecord::Base attr_accessible :mensagem, :para validates_presence_of :para validates_presence_of :mensagem validates_format_of :para, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, :allow_blank => trueend
@dudumendes
BibliografiaBEHAVIOUR-DRIVEN DEVELOPMENT. http://behaviour-driven.org/.
CHELIMSKY, David. The RSpec Book. PragBook, 2011.
JBEHAVE. http://jbehave.org/reference/.
PUGH, Ken. Lean-Agile Acceptance Test-Driven Development: better software through collaboration. Addison-Wesley, 2010.