RSpec com doubles

559 visualizações

Publicada em

Apresentação dos métodos disponíveis no RSpec para métodos "doubles". Aplicações de mocks no contexto de testes

Publicada em: Tecnologia
0 comentários
0 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Sem downloads
Visualizações
Visualizações totais
559
No SlideShare
0
A partir de incorporações
0
Número de incorporações
3
Ações
Compartilhamentos
0
Downloads
28
Comentários
0
Gostaram
0
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

RSpec com doubles

  1. 1. DesenvolvimentoBaseado em TestesRSpec - DoublesEduardo Mendesedumendes@gmail.com
  2. 2. @dudumendesIntrodução
  3. 3. @dudumendesIntroduçãoO que se quer de um bom projetoPrincípios para alcançarSituações que esclareçam
  4. 4. @dudumendesRSpec::Mocks
  5. 5. @dudumendesCriando doublesmé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. 6. @dudumendesMétodos StubsMé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
  7. 7. @dudumendesStub com classes inexistentes
  8. 8. @dudumendes Classe inexistente Identificador do mockdescribe "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") endend
  9. 9. @dudumendes Classe inexistente / atalhodescribe "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") endend
  10. 10. @dudumendescandidato = 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. 11. @dudumendesUtilizando o subject
  12. 12. @dudumendeso método subjectSubject 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. 13. @dudumendesdescribe 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 endend Professor eh uma instancia de Professor nao deve ser um aluno nao possui nome definido
  14. 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. 15. @dudumendesExercício 1
  16. 16. @dudumendesFuncionarioCrie specs com mocks para a classe funcionario eexercite 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. 17. @dudumendesmétodo and_return
  18. 18. @dudumendesretornando vários valoresmétodo and_return O and_return é uma alternativa para definição do valor a ser retornado possibilita a passagem de vários valores
  19. 19. describe UrnaEletronica do it "retorna um voto" do subject.stub(:apurar).and_return("Candidato 1") expect(subject.apurar).to eql("Candidato 1") endend
  20. 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") endend
  21. 21. @dudumendesmétodo stub_chain
  22. 22. @dudumendestestando a intimidademétodo stub_chain O stub_chain permite verificar o valor final retornado de uma chamada em cadeia de métodos
  23. 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) endend
  24. 24. @dudumendesmétodo any_instance
  25. 25. @dudumendestestando instâncias aleatóriasmétodo any_instance O any_instance cria expectativas sobre qualquer objeto de um classe
  26. 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 endend
  27. 27. @dudumendesPassando argumentos
  28. 28. @dudumendespassando argumentosmé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. 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 endend
  30. 30. describe Candidato do it "inicializa com um numero" do Candidato.stub(:new).with(:numero => 99) Candidato.new :numero => 99 endend
  31. 31. it "inicializa com qualquer valor" do Candidato.stub(:new).with(any_args) Candidato.new Candidato.new(:nome => "Valor", :idade => 19) end
  32. 32. it "inicializa com nome especifico" do Candidato.stub(:new).with( hash_including(:nome => "Joao Luiz")) Candidato.new(:nome => "Joao Luiz", :idade => 19) end
  33. 33. it "inicializa com nome especifico" do Candidato.stub(:new).with( hash_not_including(:nome => "Joao Luiz")) Candidato.new(:idade => 19) end
  34. 34. it "escreve um nome" do subject.stub(:nome=).with(/Joao/) subject.nome= "Joao Luiz"end
  35. 35. it "o nome deve ser uma String" do subject.stub(:nome=).with(instance_of(String)) subject.nome= 3 end
  36. 36. it "deve ser ficha limpa" do subject.stub(:ficha_limpa=).with(boolean) subject.ficha_limpa=true end
  37. 37. describe Candidato do it "inicializa com qualquer valor" do Candidato.stub(:new).with(anything) Candidato.new(:nome => "Valor", :idade => 19) endend
  38. 38. @dudumendesRetorno dependente do argumento
  39. 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" endend
  40. 40. @dudumendesStub de exceções
  41. 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) endend
  42. 42. @dudumendesCombinando classes
  43. 43. @dudumendesdescribe 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" endend
  44. 44. @dudumendesdescribe 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" endendInscricao: sujeitoCandidato: não é o foco do exemplo, coloboradorimediatoTeste double para atuar como um candidato
  45. 45. @dudumendesExercício 2
  46. 46. @dudumendesInscricao A partir do spec da Inscricao, crie uma classe Inscricao que faça o teste passar
  47. 47. @dudumendesdescribe 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" endend
  48. 48. @dudumendesEstraté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. 49. @dudumendesEstraté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. 50. @dudumendesMessage Expectations
  51. 51. @dudumendesExpectativas de mensagensmétodo should_receive should_receive caso a mensagem programada nunca seja chamada o método lançará um erro o teste falhará
  52. 52. @dudumendesdescribe 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" endend
  53. 53. @dudumendesclass Inscricao def initialize(candidato) @candidato = candidato end def gerar "Inscricao de Luiz" endend 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. 54. @dudumendesStubs + Message Expectations
  55. 55. @dudumendesStubs + 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
  56. 56. @dudumendesit "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
  57. 57. @dudumendesIntenção no códigoSujeito InscriçãoColaborador primário logger -- mockColaborador secundário Candidato -- stub
  58. 58. @dudumendesExercício 3
  59. 59. @dudumendesInscricao Adicione o exemplo do log no spec e o faça passar
  60. 60. @dudumendesCounts
  61. 61. @dudumendesCountsshould_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
  62. 62. @dudumendesdescribe "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. 63. @dudumendesdescribe "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. 64. @dudumendes exactly().timesdescribe "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. 65. @dudumendes at_most().timesdescribe "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 endend
  66. 66. @dudumendes at_least().timesdescribe "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 endend
  67. 67. @dudumendes once, twicedescribe "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. 68. @dudumendesit "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.pingend
  69. 69. @dudumendesExpectativas negativas
  70. 70. @dudumendesExpectativas negativasshould_not_receive should_not_receive Utilizado quando não queremos que determinado sujeito receba uma mensagem durante o exemplo
  71. 71. @dudumendesit "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.pingendit "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.pingend
  72. 72. @dudumendesMensagem ordenadas
  73. 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. 74. @dudumendesExercício 4
  75. 75. @dudumendesTransferencia 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. 76. @dudumendesTransferencia 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. 77. @dudumendesUtilização de Mocks
  78. 78. @dudumendesIsolar dependênciasCódigo fracamente acoplado possui dependências Se os objetos são fáceis e “baratos” de construir não utilize mocks ou stubs
  79. 79. @dudumendesIsolar dependênciasDependê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. 80. @dudumendes BD Interface para BDSujeito Interface de rede Web
  81. 81. @dudumendes Stub Interface para BDExemplo Sujeito Stub Interface de rede
  82. 82. @dudumendesIsolação de comportamentosnã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
  83. 83. @dudumendesNão determinismo local Sujeito Dado
  84. 84. @dudumendes Stub doExemplo Sujeito Dado 3,5,6,6,6,7,10
  85. 85. @dudumendesProgresso semimplementaçõesÀs vezes, dependemos de comportamentos deobjetos 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. 86. @dudumendesDescobrimento de interfaceAo 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
  87. 87. @dudumendesFocos
  88. 88. @dudumendesFoco no PapelMockar objetos permite a concentração no queimporta no que o objeto faz e não no que ele é
  89. 89. @dudumendesit "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
  90. 90. @dudumendesFocar na interaçãoao 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
  91. 91. @dudumendesdescribe 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" endend
  92. 92. @dudumendesdescribe 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" endend
  93. 93. @dudumendesFocar na interaçãoao invés do estado
  94. 94. @dudumendesBibliografia 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/

×