rspec - desenvolvimento baseado em teste

Post on 07-Dec-2014

1.129 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

DesenvolvimentoBaseado em TestesRSpec - 1.ª parteEduardo Mendesedumendes@gmail.com

@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