Desenvolvimento
Baseado em Testes
RSpec - Doubles
Eduardo Mendes
edumendes@gmail.com
@dudumendes




Introdução
@dudumendes




Introdução


O que se quer de um bom projeto
Princípios para alcançar
Situações que esclareçam
@dudumendes




RSpec::Mocks
@dudumendes




Criando doubles
método double

 algum_double = double(“um_double”)
 algum_stub = stub(“um_stub”)
 algum_mock = mock(“um_mock”)


   Argumento string é opcional, mas recomendado
     pode ser utilizado um símbolo
   Utilizado na mensagens de falha
   Geram instâncias de RSpec::Mocks::Mock
@dudumendes




Métodos Stubs

Método em que se pode programar uma resposta pré-
definida de um objeto,
  que será retornada durante a execução de exemplo
  utiliza-se quando não se tem expectativas sobre a
  execução
@dudumendes




Stub com classes inexistentes
@dudumendes




         Classe inexistente
                                 Identificador do mock
describe "classe Candidato" do
                                     métodos e retornos
	 it "retorna nome e partido" do
	 	 candidato = mock(:candidato)
	 	 candidato.stub(:nome => "Luiz Augusto",
                    :partido => "PRAONDEEH")

	 	 expect(candidato.nome).to eql("Luiz Augusto")
	 	 expect(candidato.partido).to eql("PRAONDEEH")
	 end
end
@dudumendes




  Classe inexistente / atalho
describe "classe Candidato" do    Identificador do mock
	 it "retorna nome e partido" do       métodos e retornos
	 	 candidato = mock(:candidato,
                      :nome => "Luiz Augusto",
                      :partido => "PRAONDEEH")
	 	
	 	 expect(candidato.nome).to eql("Luiz Augusto")
	 	 expect(candidato.partido).to eql("PRAONDEEH")
	 end
end
@dudumendes




candidato = mock(:candidato)
candidato.stub(:nome => "Luiz Augusto",
                    :email => "PRAONDEEH")

candidato = mock(:candidato)
candidato.stub(:nome).and_return("Luiz Augusto")
candidato.stub(:email).and_return("PRAONDEEH")

candidato = mock(:candidato,
                      :nome => "Luiz Augusto",
                      :partido => "PRAONDEEH")
@dudumendes




Utilizando o subject
@dudumendes




o método subject
Subject
 O subject de um exemplo é o objeto que está sendo
 descrito, exercitado
 Se o subject é uma classe chamada Usuario
   uma instância de Usuario é fornecida
   automaticamente pelo método subject
   subjects são instanciados nos blocos before
@dudumendes


describe Professor do
	 it "eh uma instancia de Professor" do
	 	 expect(subject).to be_a(Professor)
	 end
	
	 it "nao deve ser um aluno" do
	 	 expect(subject).not_to be_an(Aluno)
	 end
	
	 it "nao possui nome definido" do
	 	 expect(subject.nome).to be_nil
	 end
end                         Professor
	                            eh uma instancia de Professor
                               nao deve ser um aluno
                               nao possui nome definido
describe Candidato do                                         @dudumendes
	 it "possui email" do
	 	 subject.stub(:email => "luiz@email.com")
	 	 expect(subject.email).to eql("luiz@email.com")
	 end
	
	 it "pode ter partido nulo" do
	 	 subject.stub(:partido)
	 	 expect(subject.partido).to be_nil
	 end

	   it "possui email alternativo" do
	   	 subject.stub(:emailAlternativo) do
	   	 	 "luiz@alternativo.com"
	   	 end
	   	
	   	 expect(subject.emailAlternativo).to eql("luiz@alternativo.com")
	   end

	 it "possui email e partido" do
	 	 subject.stub(:email => "luiz@email.com", :partido => "PUTZ")
	 	 expect(subject.email).to eql("luiz@email.com")
	 	 expect(subject.partido).to eql("PUTZ")
	 end	
end
@dudumendes




Exercício 1
@dudumendes




Funcionario
Crie specs com mocks para a classe funcionario e
exercite expectativas em valores pré-configurados
  Faça os testes falharem e passarem para comparar
  os resultados
  Crie 02 versões
    um spec com uma classe que não existe
    e outro com utilizando o subject
@dudumendes




método and_return
@dudumendes




retornando vários valores
método and_return




 O and_return
   é uma alternativa para definição do valor a ser
   retornado
   possibilita a passagem de vários valores
describe UrnaEletronica do
	 it "retorna um voto" do
	 	 subject.stub(:apurar).and_return("Candidato 1")
	 	 expect(subject.apurar).to eql("Candidato 1")
	 end
end
describe UrnaEletronica do
	 it "retorna votos em sequencia" do
	 	 subject.stub(:apurar).and_return("C1", "C2", "C3")
	 	
	 	 expect(subject.apurar).to eql("C1")
	 	 expect(subject.apurar).to eql("C2")
	 	 expect(subject.apurar).to eql("C3")
	 end
end
@dudumendes




método stub_chain
@dudumendes




testando a intimidade
método stub_chain




 O stub_chain
   permite verificar o valor final retornado de uma
   chamada em cadeia de métodos
describe "classe Estacao" do
	 it "retorna a previsao de temperatura maxima" do
	 	 subject.stub_chain(:termometro, :maxima => 32)
	 	 expect(subject.termometro.maxima).to eql(32)
	 end
	
	 it "retorna a previsao de temperatura minima" do
	 	 subject.stub_chain(:termometro, :minima => 32)
	 	 expect(subject.termometro.minima).to eql(32)
	 end
end
@dudumendes




método any_instance
@dudumendes




testando instâncias aleatórias
método any_instance




 O any_instance
   cria expectativas sobre qualquer objeto de um classe
describe Eleitor do
	 it "deve votar" do
	 	 Eleitor.any_instance.stub(:votar => true)
	 	
	 	 eleitor = Eleitor.new
	 	 expect(eleitor.votar).to be_true
	 	
	 	 novo_eleitor = Eleitor.new
	 	 expect(novo_eleitor.votar).to be_true
	 	
	 end
end
@dudumendes




Passando argumentos
@dudumendes




passando argumentos
método with

 O with
   passa os parâmetros que devem ser passados a um
   método de stub
     valor
     hash
     anything, any_args, hash_including(),
     hash_not_including(), instance_of
describe Eleitor do
	 it "deve votar" do
	
	 Eleitor.any_instance.stub(:votar).with(:voto).and_return(true)
	 	 eleitor = Eleitor.new
	 	 expect(eleitor.votar(:voto)).to be_true
	 	
	 	 novo_eleitor = Eleitor.new
	 	 expect(novo_eleitor.votar(:voto)).to be_true
	 	
	 end
end
describe Candidato do
	 it "inicializa com um numero" do
	 	 Candidato.stub(:new).with(:numero => 99)
	 	 Candidato.new :numero => 99
    end
end
it "inicializa com qualquer valor" do
	 	 Candidato.stub(:new).with(any_args)

	 	 Candidato.new
	 	 Candidato.new(:nome => "Valor", :idade => 19)
	 end
it "inicializa com nome especifico" do
	 	 Candidato.stub(:new).with(
           hash_including(:nome => "Joao Luiz"))

	 	 Candidato.new(:nome => "Joao Luiz", :idade => 19)
	 end
it "inicializa com nome especifico" do
	 	 Candidato.stub(:new).with(
           hash_not_including(:nome => "Joao Luiz"))

	 	 Candidato.new(:idade => 19)
	 end
it "escreve um nome" do
	 	 subject.stub(:nome=).with(/Joao/)

	 	 subject.nome= "Joao Luiz"
end
it "o nome deve ser uma String" do
	 	 subject.stub(:nome=).with(instance_of(String))

	 	 subject.nome= 3
	 end
it "deve ser ficha limpa" do
	 	 subject.stub(:ficha_limpa=).with(boolean)

	 	 subject.ficha_limpa=true
	 end
describe Candidato do
	 it "inicializa com qualquer valor" do
	 	 Candidato.stub(:new).with(anything)

	 	 Candidato.new(:nome => "Valor", :idade => 19)
	 end
end
@dudumendes




Retorno dependente do argumento
describe "Bar" do                              @dudumendes



	 it "so pode vender para maior de 18" do
	 	 cliente = double(:cliente)
	 	
	 	 cliente.stub(:beber) do |idade|
	 	 	 if idade >= 18
	 	 	 	 "OK"
	 	 	 else
	 	 	 	 "ERROR"
	 	 	 end
	 	 end
	 	
	 	 expect(cliente.beber(20)).to eql "OK"
	 	 expect(cliente.beber(10)).to eql "ERROR"
	 end
end
@dudumendes




Stub de exceções
describe Eleitor do
  it "raises" do
    subject.stub(:idade).and_raise("Nao implementado")
	   expect { subject.idade }.to raise_error("Nao implementado")
  end

  it "throws" do
    subject.stub(:votar).and_throw(:nao_comparecimento)
    expect { subject.votar }.to throw_symbol(:nao_comparecimento)
	 end
end
@dudumendes




Combinando classes
@dudumendes




describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do

	 	 candidato = double("candidato")
	 	 candidato.stub(:nome).and_return("Luiz")

	 	 inscricao = Inscricao.new(candidato)
	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"

	 end
end
@dudumendes



describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do
	 	 candidato = double("candidato")
	 	 candidato.stub(:nome).and_return("Luiz")

	 	 inscricao = Inscricao.new(candidato)
	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"
	 end
end

Inscricao: sujeito
Candidato: não é o foco do exemplo, coloborador
imediato

Teste double para atuar como um candidato
@dudumendes




Exercício 2
@dudumendes




Inscricao



 A partir do spec da Inscricao, crie uma classe Inscricao
 que faça o teste passar
@dudumendes




describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do
	 	 candidato = double("candidato")
	 	 candidato.stub(:nome).and_return("Luiz")

	 	 inscricao = Inscricao.new(candidato)
	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"
	 end
end
@dudumendes




Estratégias
 Triangulação
   Criar um outro exemplo utilizando um valor diferente que
   força a generalização do método


 Verificacar duplicação
   Verifica-se que "Inscricao de Luiz" é uma duplicação
     aparece no spec e no método
     consequência: remoção
@dudumendes




Estratégias

 Triangulação
   Exige 02 exemplos para que o sujeito tenha o
   comportamento esperado
 Verificacar duplicação
   Pode legar valores “hard-coded” à implementação
@dudumendes




Message Expectations
@dudumendes




Expectativas de mensagens
método should_receive




 should_receive
   caso a mensagem programada nunca seja chamada
     o método lançará um erro
     o teste falhará
@dudumendes




describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do
	 	 candidato = double("candidato")
	 	 candidato.should_receive(:nome).and_return("Luiz")

	 	 inscricao = Inscricao.new(candidato)

	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"
	 end
end
@dudumendes



class Inscricao
	 def initialize(candidato)
	 	 @candidato = candidato
	 end
	
	 def gerar
	 	 "Inscricao de Luiz"
	 end
end

      Failure/Error: candidato.should_receive(:nome).and_return("Luiz")
        (Double "candidato").nome(any args)
            expected: 1 time
            received: 0 times
      # ./inscricao_spec_2.rb:14:in `block (2 levels) in <top (required)>'

      Finished in 0.00216 seconds
      1 example, 1 failure
@dudumendes




Stubs + Message Expectations
@dudumendes




Stubs + Message Expectations


 O sentido de existir métodos que retornam o mesmo
 objeto
   dar mais semântica ao teste
   identificar sujeito e colaboradores
   intenção incorporada no código
@dudumendes




it "cria um log quando o gerar eh chamado" do
	 candidato = stub("candidato")
	 candidato.stub(:nome).and_return("Luiz")

	 logger = mock("logger")
	 inscricao = Inscricao.new(candidato, logger)
	
	 logger.should_receive(:log).with("Inscricao de Luiz")
	
	 inscricao.gerar
	 	
end
@dudumendes




Intenção no código
Sujeito
  Inscrição
Colaborador primário
  logger -- mock
Colaborador secundário
  Candidato -- stub
@dudumendes




Exercício 3
@dudumendes




Inscricao



 Adicione o exemplo do log no spec e o faça passar
@dudumendes




Counts
@dudumendes




Counts

should_receive
  A expectativa default gerada por uma chamada a
  should_receive é que a mensagem seja chamada
  apenas 01 única vez
  é possível configurar o número de vezes através de
  métodos como exactly(), at_least(), at_most(),
  once, twice, combinados com o método times
@dudumendes




describe "classe Aluno" do
	 it "chama new" do
	 	 aluno = double(:aluno)
	 	 aluno.should_receive(:nome).and_return("Jessica")
	 	
	 	 expect(aluno.nome).to eql "Jessica"
	 	
	 end
	
end
@dudumendes




describe "classe Aluno" do
	 it "chama new" do
	 	 aluno = double(:aluno)
	 	 aluno.should_receive(:nome).and_return("Jessica")
	 	
	 	 expect(aluno.nome).to eql "Jessica"
	 	 expect(aluno.nome).to eql "Jessica"
	 end
	
end
@dudumendes




                     exactly().times
describe "classe Aluno" do
	 it "chama new" do
	 	 aluno = double(:aluno)
	 	 aluno.should_receive(:nome)
       .and_return("Jessica").exactly(1).times
	 	
	 	 expect(aluno.nome).to eql "Jessica"
	 	
	 end
	
end
@dudumendes




                     at_most().times
describe "Rede" do
	 it "deve ser solicitadas no maximo 5 conexoes" do
	 	 rede = double(:rede)
	 	 rede.should_receive(:open_connection).at_most(4).times
	 	
	 	 rede.open_connection
	 	 rede.open_connection
	 	 rede.open_connection
	 	 rede.open_connection
	 	
	 end
end
@dudumendes




                      at_least().times
describe "Rede" do
	 it "deve ser solicitadas no maximo 5 conexoes" do
	 	 rede = double(:rede)
	
	 rede.should_receive(:open_connection).at_least(2).times
	 	
	 	 rede.open_connection
	 	 rede.open_connection
	 	 rede.open_connection
	 	 rede.open_connection
	 	
	 end
end
@dudumendes




                         once, twice
describe "Conta" do
	 it "deve gerar extrato 01 vez" do
	 	 conta = double(:conta)
	 	 conta.should_receive(:gerar_extrato).once
	 	 conta.gerar_extrato
	 end

	 it "deve checar valor 02 vezes" do
	 	 conta = double(:conta)
	 	 conta.should_receive(:checar_valor).twice

	 	 conta.checar_valor
	 	 conta.checar_valor
	 end
@dudumendes




it "nao deve abrir conexao apos ping falso" do
	 @rede.stub(:ping).and_return(false)

	 @rede.should_receive(:open_connection).exactly(0).times
	 	 	
	 @rede.open_connection if @rede.ping
end
@dudumendes




Expectativas negativas
@dudumendes




Expectativas negativas
should_not_receive




 should_not_receive
   Utilizado quando não queremos que determinado
   sujeito receba uma mensagem durante o exemplo
@dudumendes




it "nao deve abrir conexao apos ping falso" do
	 @rede.stub(:ping).and_return(false)

	 @rede.should_not_receive(:open_connection)
	 	 	
	 @rede.open_connection if @rede.ping
end


it "nao deve abrir conexao apos ping falso" do
	 @rede.stub(:ping).and_return(false)

	 @rede.should_receive(:open_connection).never
	 	 	
	 @rede.open_connection if @rede.ping
end
@dudumendes




Mensagem ordenadas
it   "deve fazer campanha antes de votar" do
	   	    subject.stub(:fazer_campanha).ordered
	   	    subject.stub(:votar).ordered
	   	
	   	    subject.fazer_campanha
	   	    subject.votar

	 end
@dudumendes




Exercício 4
@dudumendes




Transferencia
 Contexto
   Testar a transferencia de valores entre 02 contas
   A transferência é realizada por um objeto chamado
   Transferencia
   O objeto guarda as 02 contas e executa uma
   transferência de valores entre elas
   O sujeito a se testar é o objeto Transferencia
   As contas ainda não estão implementadas
@dudumendes




Transferencia
 Exemplos
   o objeto Transferencia deve ser criado com 01 conta de origem, 01 conta de
   destino e um valor
   ao se executar a transferência, a conta de origem deve receber a mensagem
   transferir
   o 1.º argumento deve ser a conta de destino
   o 2.º argumento deve uma instância de Fixnum
   o 2.º argumento deve ter o valor
   deve ser lançado um erro com a mensagem “Saldo insuficiente”, caso o saldo
   da conta de origem seja menor que o valor solicitado
   o saldo deve ser conferido antes de transferir
@dudumendes




Utilização de Mocks
@dudumendes




Isolar dependências

Código fracamente acoplado
  possui dependências
  Se os objetos são fáceis e “baratos” de construir
    não utilize mocks ou stubs
@dudumendes




Isolar dependências
Dependências problemáticas
    configuração e construção cara
    funcionamento lento
    dependência de sistemas externos
      rede, servidores, sistema de arquivos
  Mock para isolar os exemplos das dependências e
  incrementar potenciais pontos de falha
@dudumendes




                               BD
          Interface para BD

Sujeito
          Interface de rede   Web
@dudumendes




                           Stub
                    Interface para BD
Exemplo   Sujeito
                           Stub
                    Interface de rede
@dudumendes



Isolação de comportamentos
não determinísticos

 Dependência de sistemas externos
   pode ser fonte de não determinismo
       arquivos corrompidos, falhas de disco, time out
       de rede
     MOCK e obtenha um ambiente controlado
@dudumendes




Não determinismo local


     Sujeito   Dado
@dudumendes




                    Stub do
Exemplo   Sujeito    Dado
                              3,5,6,6,6,7,10
@dudumendes



Progresso sem
implementações

Às vezes, dependemos de comportamentos de
objetos que outros times não implementaram ainda
  As interfaces podem já ter sido projetadas
  Oportunidade para explorar dependências e
  possibilidades de interfaces alternativas
@dudumendes




Descobrimento de interface

Ao exercitar a implementação de um objeto
  pode-se descobrir que ele necessita de
  comportamento de um outro que ainda não existe
    método não pensado na fase de projeto
    até mesmo objeto
@dudumendes




Focos
@dudumendes




Foco no Papel


Mockar objetos permite a concentração no que
importa
  no que o objeto faz e não no que ele é
@dudumendes




it "cria um log quando o gerar eh chamado" do
	 candidato = stub("candidato")
	 candidato.stub(:nome).and_return("Luiz")

	 logger = mock("logger")
	 inscricao = Inscricao.new(candidato, logger)
	
	 logger.should_receive(:log).with("Inscricao de Luiz")
	
	 inscricao.gerar
	 	
end
@dudumendes




Focar na interação
ao invés do estado


 Sistemas orientados a objetos dizem respeito à
 interfaces e interações
    O estado não faz parte do comportamento
    observável
    Exemplos serão menos frágeis
      se evitar o foco no estado
@dudumendes




describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do
	 	 candidato = stub("candidato", :nome => "Luiz")
	 	
	 	 inscricao = Inscricao.new(candidato)

	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"
	 end
end
@dudumendes




describe Inscricao do
	 it "utiliza o nome do candidato no cabecalho" do
	 	 candidato = double("candidato")
	 	 candidato.should_receive(:nome).and_return("Luiz")

	 	 inscricao = Inscricao.new(candidato)

	 	 expect(inscricao.gerar).to eql "Inscricao de Luiz"
	 end
end
@dudumendes




Focar na interação
ao invés do estado
@dudumendes




Bibliografia

 FOWLER, Martin. “Mocks aren’t Stubs”.
 FREEMAN, Steve; PRYCE, Nat. Growing Object-
 Oriented Software, Guiaded by Tests. Addison-Wesley.
 MESZAROS, Gerard. xUnit Test Patterns:
 RefactoringTest Code. Addison-Wesley: 2007
 MESZAROS, Gerard. xUnitTest Patterns.com. http://
 xunitpatterns.com/

RSpec com doubles

  • 1.
    Desenvolvimento Baseado em Testes RSpec- Doubles Eduardo Mendes edumendes@gmail.com
  • 2.
  • 3.
    @dudumendes Introdução O que sequer de um bom projeto Princípios para alcançar Situações que esclareçam
  • 4.
  • 5.
    @dudumendes Criando doubles método double algum_double = double(“um_double”) algum_stub = stub(“um_stub”) algum_mock = mock(“um_mock”) Argumento string é opcional, mas recomendado pode ser utilizado um símbolo Utilizado na mensagens de falha Geram instâncias de RSpec::Mocks::Mock
  • 6.
    @dudumendes Métodos Stubs Método emque se pode programar uma resposta pré- definida de um objeto, que será retornada durante a execução de exemplo utiliza-se quando não se tem expectativas sobre a execução
  • 7.
  • 8.
    @dudumendes Classe inexistente Identificador do mock describe "classe Candidato" do métodos e retornos it "retorna nome e partido" do candidato = mock(:candidato) candidato.stub(:nome => "Luiz Augusto", :partido => "PRAONDEEH") expect(candidato.nome).to eql("Luiz Augusto") expect(candidato.partido).to eql("PRAONDEEH") end end
  • 9.
    @dudumendes Classeinexistente / atalho describe "classe Candidato" do Identificador do mock it "retorna nome e partido" do métodos e retornos candidato = mock(:candidato, :nome => "Luiz Augusto", :partido => "PRAONDEEH") expect(candidato.nome).to eql("Luiz Augusto") expect(candidato.partido).to eql("PRAONDEEH") end end
  • 10.
    @dudumendes candidato = mock(:candidato) candidato.stub(:nome=> "Luiz Augusto", :email => "PRAONDEEH") candidato = mock(:candidato) candidato.stub(:nome).and_return("Luiz Augusto") candidato.stub(:email).and_return("PRAONDEEH") candidato = mock(:candidato, :nome => "Luiz Augusto", :partido => "PRAONDEEH")
  • 11.
  • 12.
    @dudumendes o método subject Subject O subject de um exemplo é o objeto que está sendo descrito, exercitado Se o subject é uma classe chamada Usuario uma instância de Usuario é fornecida automaticamente pelo método subject subjects são instanciados nos blocos before
  • 13.
    @dudumendes describe Professor do it "eh uma instancia de Professor" do expect(subject).to be_a(Professor) end it "nao deve ser um aluno" do expect(subject).not_to be_an(Aluno) end it "nao possui nome definido" do expect(subject.nome).to be_nil end end Professor eh uma instancia de Professor nao deve ser um aluno nao possui nome definido
  • 14.
    describe Candidato do @dudumendes it "possui email" do subject.stub(:email => "luiz@email.com") expect(subject.email).to eql("luiz@email.com") end it "pode ter partido nulo" do subject.stub(:partido) expect(subject.partido).to be_nil end it "possui email alternativo" do subject.stub(:emailAlternativo) do "luiz@alternativo.com" end expect(subject.emailAlternativo).to eql("luiz@alternativo.com") end it "possui email e partido" do subject.stub(:email => "luiz@email.com", :partido => "PUTZ") expect(subject.email).to eql("luiz@email.com") expect(subject.partido).to eql("PUTZ") end end
  • 15.
  • 16.
    @dudumendes Funcionario Crie specs commocks para a classe funcionario e exercite expectativas em valores pré-configurados Faça os testes falharem e passarem para comparar os resultados Crie 02 versões um spec com uma classe que não existe e outro com utilizando o subject
  • 17.
  • 18.
    @dudumendes retornando vários valores métodoand_return O and_return é uma alternativa para definição do valor a ser retornado possibilita a passagem de vários valores
  • 19.
    describe UrnaEletronica do it "retorna um voto" do subject.stub(:apurar).and_return("Candidato 1") expect(subject.apurar).to eql("Candidato 1") end end
  • 20.
    describe UrnaEletronica do it "retorna votos em sequencia" do subject.stub(:apurar).and_return("C1", "C2", "C3") expect(subject.apurar).to eql("C1") expect(subject.apurar).to eql("C2") expect(subject.apurar).to eql("C3") end end
  • 21.
  • 22.
    @dudumendes testando a intimidade métodostub_chain O stub_chain permite verificar o valor final retornado de uma chamada em cadeia de métodos
  • 23.
    describe "classe Estacao"do it "retorna a previsao de temperatura maxima" do subject.stub_chain(:termometro, :maxima => 32) expect(subject.termometro.maxima).to eql(32) end it "retorna a previsao de temperatura minima" do subject.stub_chain(:termometro, :minima => 32) expect(subject.termometro.minima).to eql(32) end end
  • 24.
  • 25.
    @dudumendes testando instâncias aleatórias métodoany_instance O any_instance cria expectativas sobre qualquer objeto de um classe
  • 26.
    describe Eleitor do it "deve votar" do Eleitor.any_instance.stub(:votar => true) eleitor = Eleitor.new expect(eleitor.votar).to be_true novo_eleitor = Eleitor.new expect(novo_eleitor.votar).to be_true end end
  • 27.
  • 28.
    @dudumendes passando argumentos método with O with passa os parâmetros que devem ser passados a um método de stub valor hash anything, any_args, hash_including(), hash_not_including(), instance_of
  • 29.
    describe Eleitor do it "deve votar" do Eleitor.any_instance.stub(:votar).with(:voto).and_return(true) eleitor = Eleitor.new expect(eleitor.votar(:voto)).to be_true novo_eleitor = Eleitor.new expect(novo_eleitor.votar(:voto)).to be_true end end
  • 30.
    describe Candidato do it "inicializa com um numero" do Candidato.stub(:new).with(:numero => 99) Candidato.new :numero => 99 end end
  • 31.
    it "inicializa comqualquer valor" do Candidato.stub(:new).with(any_args) Candidato.new Candidato.new(:nome => "Valor", :idade => 19) end
  • 32.
    it "inicializa comnome especifico" do Candidato.stub(:new).with( hash_including(:nome => "Joao Luiz")) Candidato.new(:nome => "Joao Luiz", :idade => 19) end
  • 33.
    it "inicializa comnome especifico" do Candidato.stub(:new).with( hash_not_including(:nome => "Joao Luiz")) Candidato.new(:idade => 19) end
  • 34.
    it "escreve umnome" do subject.stub(:nome=).with(/Joao/) subject.nome= "Joao Luiz" end
  • 35.
    it "o nomedeve ser uma String" do subject.stub(:nome=).with(instance_of(String)) subject.nome= 3 end
  • 36.
    it "deve serficha limpa" do subject.stub(:ficha_limpa=).with(boolean) subject.ficha_limpa=true end
  • 37.
    describe Candidato do it "inicializa com qualquer valor" do Candidato.stub(:new).with(anything) Candidato.new(:nome => "Valor", :idade => 19) end end
  • 38.
  • 39.
    describe "Bar" do @dudumendes it "so pode vender para maior de 18" do cliente = double(:cliente) cliente.stub(:beber) do |idade| if idade >= 18 "OK" else "ERROR" end end expect(cliente.beber(20)).to eql "OK" expect(cliente.beber(10)).to eql "ERROR" end end
  • 40.
  • 41.
    describe Eleitor do it "raises" do subject.stub(:idade).and_raise("Nao implementado") expect { subject.idade }.to raise_error("Nao implementado") end it "throws" do subject.stub(:votar).and_throw(:nao_comparecimento) expect { subject.votar }.to throw_symbol(:nao_comparecimento) end end
  • 42.
  • 43.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = double("candidato") candidato.stub(:nome).and_return("Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end
  • 44.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = double("candidato") candidato.stub(:nome).and_return("Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end Inscricao: sujeito Candidato: não é o foco do exemplo, coloborador imediato Teste double para atuar como um candidato
  • 45.
  • 46.
    @dudumendes Inscricao A partirdo spec da Inscricao, crie uma classe Inscricao que faça o teste passar
  • 47.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = double("candidato") candidato.stub(:nome).and_return("Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end
  • 48.
    @dudumendes Estratégias Triangulação Criar um outro exemplo utilizando um valor diferente que força a generalização do método Verificacar duplicação Verifica-se que "Inscricao de Luiz" é uma duplicação aparece no spec e no método consequência: remoção
  • 49.
    @dudumendes Estratégias Triangulação Exige 02 exemplos para que o sujeito tenha o comportamento esperado Verificacar duplicação Pode legar valores “hard-coded” à implementação
  • 50.
  • 51.
    @dudumendes Expectativas de mensagens métodoshould_receive should_receive caso a mensagem programada nunca seja chamada o método lançará um erro o teste falhará
  • 52.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = double("candidato") candidato.should_receive(:nome).and_return("Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end
  • 53.
    @dudumendes class Inscricao definitialize(candidato) @candidato = candidato end def gerar "Inscricao de Luiz" end end Failure/Error: candidato.should_receive(:nome).and_return("Luiz") (Double "candidato").nome(any args) expected: 1 time received: 0 times # ./inscricao_spec_2.rb:14:in `block (2 levels) in <top (required)>' Finished in 0.00216 seconds 1 example, 1 failure
  • 54.
  • 55.
    @dudumendes Stubs + MessageExpectations O sentido de existir métodos que retornam o mesmo objeto dar mais semântica ao teste identificar sujeito e colaboradores intenção incorporada no código
  • 56.
    @dudumendes it "cria umlog quando o gerar eh chamado" do candidato = stub("candidato") candidato.stub(:nome).and_return("Luiz") logger = mock("logger") inscricao = Inscricao.new(candidato, logger) logger.should_receive(:log).with("Inscricao de Luiz") inscricao.gerar end
  • 57.
    @dudumendes Intenção no código Sujeito Inscrição Colaborador primário logger -- mock Colaborador secundário Candidato -- stub
  • 58.
  • 59.
    @dudumendes Inscricao Adicione oexemplo do log no spec e o faça passar
  • 60.
  • 61.
    @dudumendes Counts should_receive Aexpectativa default gerada por uma chamada a should_receive é que a mensagem seja chamada apenas 01 única vez é possível configurar o número de vezes através de métodos como exactly(), at_least(), at_most(), once, twice, combinados com o método times
  • 62.
    @dudumendes describe "classe Aluno"do it "chama new" do aluno = double(:aluno) aluno.should_receive(:nome).and_return("Jessica") expect(aluno.nome).to eql "Jessica" end end
  • 63.
    @dudumendes describe "classe Aluno"do it "chama new" do aluno = double(:aluno) aluno.should_receive(:nome).and_return("Jessica") expect(aluno.nome).to eql "Jessica" expect(aluno.nome).to eql "Jessica" end end
  • 64.
    @dudumendes exactly().times describe "classe Aluno" do it "chama new" do aluno = double(:aluno) aluno.should_receive(:nome) .and_return("Jessica").exactly(1).times expect(aluno.nome).to eql "Jessica" end end
  • 65.
    @dudumendes at_most().times describe "Rede" do it "deve ser solicitadas no maximo 5 conexoes" do rede = double(:rede) rede.should_receive(:open_connection).at_most(4).times rede.open_connection rede.open_connection rede.open_connection rede.open_connection end end
  • 66.
    @dudumendes at_least().times describe "Rede" do it "deve ser solicitadas no maximo 5 conexoes" do rede = double(:rede) rede.should_receive(:open_connection).at_least(2).times rede.open_connection rede.open_connection rede.open_connection rede.open_connection end end
  • 67.
    @dudumendes once, twice describe "Conta" do it "deve gerar extrato 01 vez" do conta = double(:conta) conta.should_receive(:gerar_extrato).once conta.gerar_extrato end it "deve checar valor 02 vezes" do conta = double(:conta) conta.should_receive(:checar_valor).twice conta.checar_valor conta.checar_valor end
  • 68.
    @dudumendes it "nao deveabrir conexao apos ping falso" do @rede.stub(:ping).and_return(false) @rede.should_receive(:open_connection).exactly(0).times @rede.open_connection if @rede.ping end
  • 69.
  • 70.
    @dudumendes Expectativas negativas should_not_receive should_not_receive Utilizado quando não queremos que determinado sujeito receba uma mensagem durante o exemplo
  • 71.
    @dudumendes it "nao deveabrir conexao apos ping falso" do @rede.stub(:ping).and_return(false) @rede.should_not_receive(:open_connection) @rede.open_connection if @rede.ping end it "nao deve abrir conexao apos ping falso" do @rede.stub(:ping).and_return(false) @rede.should_receive(:open_connection).never @rede.open_connection if @rede.ping end
  • 72.
  • 73.
    it "deve fazer campanha antes de votar" do subject.stub(:fazer_campanha).ordered subject.stub(:votar).ordered subject.fazer_campanha subject.votar end
  • 74.
  • 75.
    @dudumendes Transferencia Contexto Testar a transferencia de valores entre 02 contas A transferência é realizada por um objeto chamado Transferencia O objeto guarda as 02 contas e executa uma transferência de valores entre elas O sujeito a se testar é o objeto Transferencia As contas ainda não estão implementadas
  • 76.
    @dudumendes Transferencia Exemplos o objeto Transferencia deve ser criado com 01 conta de origem, 01 conta de destino e um valor ao se executar a transferência, a conta de origem deve receber a mensagem transferir o 1.º argumento deve ser a conta de destino o 2.º argumento deve uma instância de Fixnum o 2.º argumento deve ter o valor deve ser lançado um erro com a mensagem “Saldo insuficiente”, caso o saldo da conta de origem seja menor que o valor solicitado o saldo deve ser conferido antes de transferir
  • 77.
  • 78.
    @dudumendes Isolar dependências Código fracamenteacoplado possui dependências Se os objetos são fáceis e “baratos” de construir não utilize mocks ou stubs
  • 79.
    @dudumendes Isolar dependências Dependências problemáticas configuração e construção cara funcionamento lento dependência de sistemas externos rede, servidores, sistema de arquivos Mock para isolar os exemplos das dependências e incrementar potenciais pontos de falha
  • 80.
    @dudumendes BD Interface para BD Sujeito Interface de rede Web
  • 81.
    @dudumendes Stub Interface para BD Exemplo Sujeito Stub Interface de rede
  • 82.
    @dudumendes Isolação de comportamentos nãodeterminísticos Dependência de sistemas externos pode ser fonte de não determinismo arquivos corrompidos, falhas de disco, time out de rede MOCK e obtenha um ambiente controlado
  • 83.
  • 84.
    @dudumendes Stub do Exemplo Sujeito Dado 3,5,6,6,6,7,10
  • 85.
    @dudumendes Progresso sem implementações Às vezes,dependemos de comportamentos de objetos que outros times não implementaram ainda As interfaces podem já ter sido projetadas Oportunidade para explorar dependências e possibilidades de interfaces alternativas
  • 86.
    @dudumendes Descobrimento de interface Aoexercitar a implementação de um objeto pode-se descobrir que ele necessita de comportamento de um outro que ainda não existe método não pensado na fase de projeto até mesmo objeto
  • 87.
  • 88.
    @dudumendes Foco no Papel Mockarobjetos permite a concentração no que importa no que o objeto faz e não no que ele é
  • 89.
    @dudumendes it "cria umlog quando o gerar eh chamado" do candidato = stub("candidato") candidato.stub(:nome).and_return("Luiz") logger = mock("logger") inscricao = Inscricao.new(candidato, logger) logger.should_receive(:log).with("Inscricao de Luiz") inscricao.gerar end
  • 90.
    @dudumendes Focar na interação aoinvés do estado Sistemas orientados a objetos dizem respeito à interfaces e interações O estado não faz parte do comportamento observável Exemplos serão menos frágeis se evitar o foco no estado
  • 91.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = stub("candidato", :nome => "Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end
  • 92.
    @dudumendes describe Inscricao do it "utiliza o nome do candidato no cabecalho" do candidato = double("candidato") candidato.should_receive(:nome).and_return("Luiz") inscricao = Inscricao.new(candidato) expect(inscricao.gerar).to eql "Inscricao de Luiz" end end
  • 93.
  • 94.
    @dudumendes Bibliografia FOWLER, Martin.“Mocks aren’t Stubs”. FREEMAN, Steve; PRYCE, Nat. Growing Object- Oriented Software, Guiaded by Tests. Addison-Wesley. MESZAROS, Gerard. xUnit Test Patterns: RefactoringTest Code. Addison-Wesley: 2007 MESZAROS, Gerard. xUnitTest Patterns.com. http:// xunitpatterns.com/