DesenvolvimentoBaseado em TestesRSpec - 1.ª parteEduardo [email protected]
@dudumendes
Ferramentas para TDD
@dudumendes
Componente testado de maneira isolada
preparar
executar
validar
“resetar”
Teste Unitário
Ciclo do TDD
@dudumendes
Escreva um teste ANTESde escrever um código a ser testado
Escreva um código queapenas faça compilar o testee observe o teste funcionando
Refatore para o formato mais simples possível
Ciclo do TDD
@dudumendes
Componente testado de maneira isolada
preparar
executar
validar
“resetar”
Teste UnitárioCiclo do TDD
Frameworks xUnit
Baseado emasserções
@dudumendes
class TestNumeroSimples < Test::Unit::TestCase def test_simples assert_equal(4, NumeroSimples.new(2).add(4)) assert_equal(6, NumeroSimples.new(2).multiply(3)) endend
@dudumendes
def testeAssertivoassertTrue(valores.contains("um")|| valores.contains("dois")|| valores.contains("tres"))end
@dudumendes
xUnits e DSLs
começaram a surgir alternativas que possuíam mais legibilidade
utilização de DSLs
uma tendência das linguagens scripts é aproximar a programação da linguagem natural, facilitando o entendimento
contraponto aos xUnits
JUnit in Action
@dudumendes
Hamcrest
Framework para declarações“critérios de correspondência (match)”
Matchers (ex: hasItem, equalTo, anyOf)Informam se um determinado objeto casa ou não com algum critério
podem descrever o critério
JUnit in Action
@dudumendes
No momento RED
As mensagens de RED bar são mais amigáveis
JUnit in Action
@dudumendes
Orientação a Objetos
Orientação a objetos diz mais respeito à comunicação entre os objetos do que às
suas propriedades e comportamentos
@dudumendes
Sistema OO
É um conjunto de objetos que colaboram entre si
@dudumendes
Problemas
Quando se passa a testar a estrutura dos objetosao invés do que eles fazem
É preciso pensar nas interações entre
pessoas e sistemas
os próprios objetos
É preciso pensar no comportamento
@dudumendes
O Design Emergente
Benefícios do TDD
Qualidade de código
Fraco acoplamento
Alta Coesão
@dudumendes
RSpec
@dudumendes
Instalação
gem install rspec
@dudumendes
Instalação
rspec --help
@dudumendes
Instalação
rspec arquivo/pasta_de_spec
@dudumendes
RSpec
“RSpec is testing tool for the Ruby programming language
Born under the bannerof Behaviour-Driven Development,
it is designed to make Test-Driven Development a productive and enjoyable experience.”
Fonte: http://rspec.info/
@dudumendes
RSpec
Criado por Steven Baker, 2005
Na época já existia a ideia e praticantes de TDD
Juntou o que tinha ouvido de BDD com Aslak Hellsoy e Dave Stels
@dudumendes
RSpec
describe it
expect should
@dudumendes
describe
É o método utilizado para agrupar os testes a serem executados
os “exemplos”
testam os comportamentos dos objetos
describe “titulo” do
end
@dudumendes
itno RSpec, os testes são chamados de exemplos
it é o método que declara o exemplo
descreve o comportamento do exemplo
it “descrição do comportamento” do
end
@dudumendes
expectmétodo que verifica um critério, uma expectativa
expect(valor_obtido).to valor_esperado
@dudumendes
RSpec Hello World
@dudumendes
Regra de Ouro do TDD
“Nunca codifique uma funcionalidade nova
sem um teste falhando”
@dudumendes
describe Ligador do
it "deve dizer 'Hello turma!' quando receber a mensagem saudar" do
ligador = Ligador.new saudacao = ligador.saudar expect(saudacao).to eql "Hello turma!"
end
end
ligador_spec.rb
@dudumendes
rspec ligador_spec.rb
@dudumendes
NameError: uninitialized constant Ligador
@dudumendes
class Ligador def saudar "Hello turma!" endend
@dudumendes
rspec ligador_spec.rb
@dudumendes
Ligador deve dizer 'Hello turma!' quando receber a mensagem saudar
Finished in 0.00062 seconds1 example, 0 failures
@dudumendes
Dicionário RSpecSubject Code / Sujeito
Código que possui o comportamento a ser especificado
Expectation / Expectativas
Expressão que representa o comportamento esperado do Subject Code
Code Example / Exemplo de código
Exemplo executável de como o Subject Code pod ser utilizado e seu comportamento esperado em determinado contexto
Example Group / Grupo de exemplos
Um grupo de exemplos de código
Spec
Um arquivo que contém um ou mais grupos de exemplos
@dudumendes
Comparando
Assertions
Test method
Test case
Expectation
Code example
Example Group
@dudumendes
RSpecDescreve uma conversação
O RSpec é um DSL capaz de descrever o comportamentos dos objetos
Aproxima as descrições destes comportamentos à linguagem natural
Uma conversação
@dudumendes
DescribeDescreve um objeto
Mas como cliente e
servidor se comunicam?
Clientes e servidores devem entender
HTTP e os navegadores devem conhecer HTML
Descreva“uma
Conta”
Ela “deve iniciar com zero de
saldo”
@dudumendes
describe "Uma nova Conta" do
end
it "deve iniciar com 0 de saldo" do
end
conta = Conta.newexpect(conta.saldo).to be 0
@dudumendes
O método describeArgumentos
quantos argumentos forem necessários
normalmente: 1 ou 2
descreve o objeto em um estado específico, ou um conjunto de comportamentos esperados do mesmo
o 1.º pode ser uma referência a uma classe ou string
o 2.º, opcional, deve ser uma string
@dudumendes
O método describeshow me the code
describe “Um Usuario” { ... }
=> Um Usuario
describe Usuario { ... }
=>Usuario
describe Usuario, “sem papeis atribuidos” { ... }
=>Usuario sem papeis atribuidos
@dudumendes
describe Usuario do
end
it "nao deve acessar conteudo protegido" do
end
describe “sem papel atribuido” do
end
...
@dudumendes
O método contextalias de describe
context “sem papel atribuido” { ... }=>sem papel atribuido
torna o código mais legível
describe -> objetos, comportamentos
context -> contextos em que objetos deve ser exercitados
@dudumendes
describe Usuario do
end
it "nao deve acessar conteudo protegido" do
end
context “sem papel atribuido” do
end
...
@dudumendes
Ito comportamento que será exercitado
Mas como cliente e
servidor se comunicam?
Clientes e servidores devem entender
HTTP e os navegadores devem conhecer HTML
Descreva“uma
Conta”
Ela “deve iniciar com zero de
saldo”
@dudumendes
O método itArgumentos
uma string e um hash opcional
string
uma sentença com o comportamento do código que será exercitado
pode vários em um describe
se um bloco de código não for passado, o exemplo será marcado como pendente
@dudumendes
describe Pilha do describe "#peek" do it "deve retornar o elemento do topo" do pilha = Pilha.new pilha.push :item expect(pilha.peek).to be :item end it "nao deve remover o elemento do topo" do pilha = Pilha.new pilha.push :item expect(pilha.items.length).to be 1 end endend
@dudumendes
Pilha #peek deve retornar o elemento do topo nao deve remover o elemento do topo
rspec spec/pilha_spec.rb --format documentation
@dudumendes
Pilha #peek retorna o elemento do topo nao remove o elemento do topo
rspec spec/pilha_spec.rb --format documentation
@dudumendes
describe Pilha do describe "#peek" do it "deve retornar o elemento do topo" do pilha = Pilha.new pilha.push :item expect(pilha.peek).to be :item end it "nao deve remover o elemento do topo" do pilha = Pilha.new pilha.push :item expect(pilha.items.length).to be 1 end endend
@dudumendes
describe Pilha do pilha = Pilha.new pilha.push :item
describe "#peek" do it "deve retornar o elemento do topo" do expect(pilha.peek).to be :item end it "nao deve remover o elemento do topo" do expect(pilha.items.length).to be 1 end endend
@dudumendes
Postergando exemplos
@dudumendes
TDD por Kent BeckProjeto
Implementa
TesteTeste
@dudumendes
TDD por Kent BeckProjeto
crie uma lista de teste
anote e identifique os testes
seja conciso: uma classe ou método
posteriormente, é possível adicionar mais testes
@dudumendes
Postergando exemplos“crie uma lista de teste” “anote e identifique os testes”
O RSpec pode auxiliar a tarefa de identificação de “exemplos”
A sua lista pode ser formada pelos exemplos em um spec
03 maneiras
exemplos sem blocos
exemplos desabilitados por pending
envolver o código do exemplo com pending, para ser notificado quando estiverem FIXED
@dudumendes
Exemplo sem bloco
Contexto
Você quer criar sua lista de comportamentos a serem exercitados
Solução
Coloque somente a descrição do exemplo para montar sua lista
O RSpec avisará que o exemplo está pendente
@dudumendes
describe TvAberta do
end
it "deve ser gratuita" do ... end
it "deve possuir os canais abertos" do...
end
it "nao deve incluir os canais fechados"
@dudumendes
TvAberta deve ser gratuita deve possuir os canais abertos nao deve incluir os canais fechados (PENDING: Not yet implemented)
@dudumendes
O método pendingContexto
Você tem um exemplo falhando que ainda não tem como passar
Depende de algo ainda não resolvido
Solução
marque o exemplo com o método pending
o bloco é executado, mas para na linha onde o pending foi declarado
na saída haverá o aviso de pendência
@dudumendes
describe TvAberta do it "deve ser gratuita" do ... end it "deve possuir os canais abertos" do
... end it "nao deve incluir os canais fechados" it "deve possuir closed caption" do pending "esperando correcao de codigo" TvAberta.closed_caption endend
@dudumendes
TvAberta deve ser gratuita deve possuir os canais abertos nao deve incluir os canais fechados (PENDING: Not yet implemented) deve possuir closed caption (PENDING: esperando correcao de codigo)
Pending: TvAberta nao deve incluir os canais fechados # Not yet implemented # ./spec/tv_aberta_spec.rb:13 TvAberta deve possuir closed caption # esperando correcao de codigo # ./spec/tv_aberta_spec.rb:15
@dudumendes
O método pendingcom corpo
Contexto
Você tem um exemplo em que um bug foi detectado
Você gostaria que o código executasse mesmo com a pendência
Solução
crie um bloco com pending
o bloco é executado
se o erro ocorrer, o RSpec executa como um pending normal
caso contrário ele avisa que o exemplo está sem problemas (FIXED)
Após a detecção da correção, é possível livrar o bloco do pending
@dudumendes
describe "um array vazio" do it "deve ser vazio" do pending("bug informado 18987") do expect([]).to be_empty end endend
@dudumendes
um array vazio deve ser vazio (FAILED - 1)
Failures:
1) um array vazio deve ser vazio FIXED Expected pending 'bug informado 18987' to fail. No Error was raised. # ./spec/array_spec.rb:3:in `block (2 levels) in <top (required)>'
@dudumendes
describe "um array vazio" do it "deve ser vazio" do expect([]).to be_empty endend
@dudumendes
Hooks
@dudumendes
Componente testado de maneira isolada
preparar
executar
validar
“resetar”
04 fases
@dudumendes
Hooks
Exemplo
AfterBefore
@dudumendes
métodos before / after
métodos before e after
equivalem às fases de preparar e resetar, respectivamente
métodos que executam antes e depois de cada exemplo
ou antes e depois de grupo de exemplos
@dudumendes
método before(:each)exemplo
Contexto
alguns exemplos podem exercitar funcionalidades específicas de um mesmo objeto
ao iniciar cada exemplo este objeto deveria se encontrar no mesmo estado
Solução
coloque o código de configuração em um bloco before(:each)
Nota
o padrão do método before é o :each
pode-se utilizar somente o before
@dudumendes
describe Calculadora do describe "somar 3 e 4" do it "deve ser igual a 7" do calc = Calculadora.new expect(calc.somar(3,4)).to eql 7 end end
describe "subtrair 4 - 1" do it "deve ser igual a 3" do calc = Calculadora.new expect(calc.subtrair(4,1)).to eql 3 end endend
@dudumendes
describe Calculadora do before(:each) do @calc = Calculadora.new end
describe "somar 3 e 4" do it "deve ser igual a 7" do expect(@calc.somar(3,4)).to eql 7 end end
describe "subtrair 4 - 1" do it "deve ser igual a 3" do expect(@calc.subtrair(4,1)).to eql 3 end endend
@dudumendes
describe Calculadora do before do @calc = Calculadora.new end
describe "somar 3 e 4" do it "deve ser igual a 7" do expect(@calc.somar(3,4)).to eql 7 end end
describe "subtrair 4 - 1" do it "deve ser igual a 3" do expect(@calc.subtrair(4,1)).to eql 3 end endend
@dudumendes
método before(:each)
extraia tudo o que for de configuração dos exemplos para um método before
utilize nestes blocos variáveis de instância
@dudumendes
método before(:all)Comportamento
o código englobado por before(:all) executa apenas 01 vez antes de todos os exemplos do grupo
Cuidado
componentes devem ser testados de maneira isolada
estados compartilhados por exemplos podem gerar comportamentos inesperados
não recomendado para variáveis de instância
@dudumendes
métodos after(:each/:all)
after(:each)
executa após cada exemplo
normalmente utilizado para restaurar o estado do ambiente
after(:all)
executa após um grupo de exemplos
@dudumendes
before(:each) do @calc = Calculadora.new end after(:each) do @calc = nil end
@dudumendes
before(:each) do puts “executando before” @calc = Calculadora.new end after(:each) do puts “executando after” @calc = nil end
@dudumendes
métodos around(:each)
Contexto
um código deve ser executado antes e depois de cada exemplo
Solução
utilização de around
Pode falhar, e pode não executar o código correspondente ao after
@dudumendes
around do |example| faca_algo_antes example.run faca_algo_depois end
@dudumendes
Exceções
throw -> raise
try -> begin
catch -> rescue
finally -> ensure
@dudumendes
around do |example| begin faca_algo_antes example.run ensure faca_algo_depois end end
@dudumendes
Expectations
@dudumendes
Expectations
São a alternativa para as tradicionais asserções
Verificam se o sujeito do exemplo casa com um comportamento definido na expectativa
@dudumendes
be OK se o subject for avaliado como true
expect(true).to be trueexpect(1).to be 1
expect(1).to be > 0expect(1).to be < 2
expect(1).to be >= 1expect(1).to be <= 2
expect(true).not_to be trueexpect(1).not_to be > 2
@dudumendes
be_true OK se o subject for avaliado como true
expect(true).to be_trueexpect(1).to be_trueexpect(false).not_to be_trueexpect(nil).not_to be_true
OK se o subject for avaliado como falseexpect(false).to be_false
expect(0).to be_falseexpect(true).not_to be_false
expect(nil).not_to be_false
be_false
@dudumendes
be_nil
OK se o subject for avaliado como nil
expect(nil).to be_nil
expect(false).not_to be nil
@dudumendes
be_a be_anbe_kind_of be_a_kind_of OK se a classe do subject for uma subclasse da expectativa
expect(usuario).to be_a(Objeect)
expect(usuario).not_to be_a(Pessoa)
expect(pessoa).not_to be_an(Integer)
@dudumendes
eq, eql, equal, ==, ===alo = “alo turma!” Logica
alo.should == “alo turma!” OK se é o mesmo valor
expect(alo).to eql(alo) OK se é o mesmo valor
expect(alo).to equal(alo) OK se é o mesmo objeto
expect(alo).to_not equal(“alo turma”) OK se não é o mesmo objeto
alo.should === alo OK se é o mesmo objeto
alo.should_not === Objec.new OK se não é o mesmo objeto
@dudumendes
include OK se o subject inclui todos os elementos da expectativa
expect([1, 2, 3]).to include(1)
expect([1, 2, 3]).to include(1,3)
expect(1..3).to include(1)
expect(1..3).to include(1, 2, 3)
expect("alo turma").to include("lo tu")
expect([1,2,3]).not_to include(4)
expect("alo turma").not_to include("ali")
@dudumendes
match
OK se o subject casar com a expressão regular da expectativa
expect("alo turma").match(/alo/)
expect("alo turma").match(/ali/)
@dudumendes
raise_error OK se o bloco lançar a exceção da expectativa
expect { raise ArgumentError }.to raise_error
expect { raise ArgumentError }.to raise_error(ArgumentError)
expect { raise ArgumentError, “nome invalido”}.to raise_error(ArgumentError, "nome invalido")
expect { raise ArgumentError, “nome invalido”}.to raise_error(ArgumentError, /nome invalido/)
@dudumendes
Exercício
@dudumendes
Conversão de romanos para arábicos
Desenvolver em TDD uma funcionalidade que converta um número romano qualquer em número arábico
O que é importante saber?
@dudumendes
Romanos
Alguns caracteres possuem valores
I = 1
V = 5
X = 10
L = 50
@dudumendes
Romanos
Alguns caracteres podem se repetir
I, X, C, M
Outros não
V, L, D
@dudumendes
Romanos
Algumas combinações de caracteres retornam a soma dos elementos
II = 2
II = I + I = 1 + 1 = 2
CCC = 100 + 100 + 100 = 300
@dudumendes
Romanos
Outras combinações de caracteres retornam a subtração dos elementos
IV = 5 - 1 = 4
XL = 50 - 10 = 40
CXLIV = 100 + (50 - 10) + (5 - 1)
@dudumendes
Romanos
Algumas combinações não são possíveis
IIII
CCCC
XXXX
@dudumendes
Romanos
Entregáveis:
Spec
E a Classe RomanosParser
@dudumendes
Dicas
@dudumendes
Dicas
Exemplos
devem ser curtos e diretos
devem testar uma funcionalidade específica
Como testar
testar um pouco e codificar um pouco
@dudumendes
Dicas
Exercite o mais simples primeiro
referências e retornos vazios
coleções vazias
casos básicos de recursividade
valores limites
utilize tanto quanto possível expect()to be/eql
@dudumendes
DicasUtilize o parâmetro de mensagem
principalmente quando o teste não é tão claro
Mantenha os testes pequenos
coloque somente as expectativas necessárias para testar uma funcionalidade
Mantenha cada teste independente do outro
Evite puts
@dudumendes
Bibliografia
ASTELS, David. Test-Driven Development: A Pratical Guide. Prentice Hall, 2003.
CHELIMSKY, David. The RSpec Book. PragBook, 2011.
FREEMAN, Steve; PRYCE, Nat. Growing Object-Oriented Software, Guiaded by Tests. Addison-Wesley.
VIEIRA, Fernando. Guia Rápido de RSpec.
Top Related