Pergunta

Quando fui apresentado ao Mocks, senti que o objetivo principal era simular objetos provenientes de fontes externas de dados.Dessa forma, eu não precisava manter um banco de dados automatizado de testes de unidade, poderia simplesmente fingir.

Mas agora estou começando a pensar nisso de forma diferente.Estou me perguntando se os Mocks são mais eficazes para isolar completamente o método testado de qualquer coisa fora dele.A imagem que sempre vem à mente é o pano de fundo que você usa ao pintar.Você quer evitar que a tinta suje tudo.Estou apenas testando esse método e só quero saber como ele reage a esses fatores externos falsos.

Parece incrivelmente tedioso fazer isso dessa maneira, mas a vantagem que estou vendo é que quando o teste falha é porque está bagunçado e não 16 camadas abaixo.Mas agora preciso de 16 testes para obter a mesma cobertura de testes porque cada peça seria testada isoladamente.Além disso, cada teste se torna mais complicado e mais profundamente ligado ao método que está sendo testado.

Parece certo para mim, mas também parece brutal, então quero saber o que os outros pensam.

Foi útil?

Solução

Eu recomendo que você dê uma olhada no artigo de Martin Fowler Simulações não são esboços para um tratamento mais autoritário de Mocks do que posso lhe dar.

O objetivo dos mocks é testar a unidade do seu código isoladamente das dependências, para que você possa realmente testar um trecho de código no nível da "unidade".O código em teste é real, e todos os outros trechos de código dos quais ele depende (por meio de parâmetros ou injeção de dependência, etc.) são um "Mock" (uma implementação vazia que sempre retorna valores esperados quando um de seus métodos é chamado).

Mocks podem parecer tediosos no início, mas tornam os testes de unidade muito mais fáceis e robustos quando você pega o jeito de usá-los.A maioria das linguagens possui bibliotecas Mock que tornam a zombaria relativamente trivial.Se você estiver usando Java, recomendarei meu favorito: EasyMock.

Deixe-me terminar com este pensamento:você também precisa de testes de integração, mas ter um bom volume de testes unitários ajuda a descobrir qual componente contém um bug, quando existe.

Outras dicas

Não siga pelo caminho sombrio, Mestre Luke.:) Não zombe de tudo.Você poderia, mas não deveria...aqui está o porquê.

  • Se você continuar testando cada método isoladamente, terá surpresas e trabalho pela frente ao reuni-los todos no final. BIG BANG.Construímos objetos para que eles possam trabalhar juntos para resolver um problema maior.Por si só são insignificantes.Você precisa saber se todos os colaboradores estão trabalhando conforme o esperado.
  • Simulações tornar os testes frágeis introduzindo duplicação - Sim, eu sei que isso parece alarmante.Para cada simulação que você espera configurar, há n locais onde existe a assinatura do seu método.O código real e suas expectativas simuladas (em vários testes).Alterar o código real é mais fácil...atualizar todas as expectativas simuladas é tedioso.
  • Seu o teste agora está a par de informações privilegiadas de implementação.Portanto, seu teste depende de como você escolheu implementar a solução...ruim.Os testes devem ser especificações independentes que podem ser atendidas por múltiplas soluções.Eu deveria ter a liberdade de simplesmente pressionar delete em um bloco de código e reimplementar sem tendo que reescrever o conjunto de testes.porque os requisitos ainda permanecem os mesmos.

Para encerrar, direi "Se grasna como um pato, anda como um pato, então provavelmente é um pato" - Se parecer errado.provavelmente é.* Use simulações para abstrair filhos problemáticos, como operações de IO, bancos de dados, componentes de terceiros e similares.Assim como o sal, parte dele é necessário.demais e :x *
Esta é a guerra santa entre testes baseados em estado e testes baseados em iteração.Pesquisar no Google lhe dará uma visão mais profunda.

Esclarecimento:Estou encontrando alguma resistência.testes de integração aqui :) Então, para esclarecer minha posição ..

  • Os simulados não aparecem na região 'Testes de aceitação'/Integração.Você só os encontrará no mundo dos testes unitários.e esse é o meu foco aqui.
  • Os testes de aceitação são diferentes e são muito necessários - sem menosprezá-los.Mas os testes unitários e os testes de aceitação são diferentes e devem ser mantidos diferentes.
  • Todos os colaboradores dentro de um componente ou pacote não precisam estar isolados uns dos outros.Como a micro-otimização que é um exagero.Eles existem para resolver um problema junto..coesão.

Sim eu concordo.Vejo a zombaria às vezes dolorosa, mas muitas vezes necessária, para que seus testes realmente se tornem unidade testes, ou seja,apenas a menor unidade com a qual você pode fazer o teste está em teste.Isso permite eliminar quaisquer outros fatores que possam afetar o resultado do teste.Você acaba com muito mais testes pequenos, mas fica muito mais fácil descobrir onde está o problema no seu código.

Minha filosofia é que você deve escrever código testável para se adequar aos testes,
não escreva testes para ajustar o código.

Quanto à complexidade, minha opinião é que os testes deveriam ser simples de escrever, simplesmente porque você escreve mais testes, se forem.

Posso concordar que seria uma boa ideia se as classes que você está zombando não tivessem um conjunto de testes, porque se elas tivessem um conjunto de testes adequado, você saberia onde está o problema sem isolamento.

A maior parte do tempo que usei para objetos simulados foi quando o código para o qual estou escrevendo testes está fortemente acoplado (leia-se:design ruim), que tenho que escrever objetos simulados quando as classes das quais eles dependem não estão disponíveis.Claro que existem usos válidos para objetos simulados, mas se o seu código requer seu uso, eu daria outra olhada no design.

Sim, essa é a desvantagem de testar com simulações.Há muito trabalho que você precisa fazer para parecer brutal.Mas essa é a essência do teste unitário.Como você pode testar algo isoladamente se não zomba de recursos externos?

Por outro lado, você está zombando de funcionalidades lentas (como bancos de dados e operações de E/S).Se os testes forem executados mais rapidamente, os programadores ficarão satisfeitos.Não há nada mais doloroso do que esperar por testes realmente lentos, que levam mais de 10 segundos para terminar a execução, enquanto você tenta implementar um recurso.

Se todos os desenvolvedores do seu projeto gastassem tempo escrevendo testes unitários, então essas 16 camadas (de indireção) não seriam um grande problema.Esperamos que você tenha essa cobertura de teste desde o início, certo?:)

Além disso, não se esqueça de escrever um teste de função/integração entre objetos em colaboração.Ou então você pode perder alguma coisa.Esses testes não precisarão ser executados com frequência, mas ainda assim são importantes.

Em uma escala, sim, os mocks devem ser usados ​​para simular fontes de dados externas, como um banco de dados ou um serviço web.Em uma escala mais refinada, no entanto, se você estiver projetando código fracamente acoplado, poderá desenhar linhas em todo o código quase arbitrariamente sobre o que pode, a qualquer momento, ser um 'sistema externo'.Veja um projeto no qual estou trabalhando atualmente:

Quando alguém tenta fazer check-in, o CheckInUi envia um CheckInInfo opor-se a um CheckInMediador objeto que o valida usando um CheckInValidator, então se estiver ok, ele preenche um objeto de domínio chamado Transação com CheckInInfo usando CheckInInfoAdapter então passa o Transação para uma instância de ITransactionDao.SaveTransaction() para persistência.

Estou agora escrevendo alguns automatizados testes de integração e obviamente o CheckInUi e ITransactionDao são janelas para sistemas externos e são eles que devem ser ridicularizados.No entanto, quem pode dizer isso em algum momento CheckInValidator não fará uma chamada para um serviço da web?É por isso que quando você escreve testes unitários você assume que tudo, exceto a funcionalidade específica da sua classe, é um sistema externo.Portanto, no meu teste unitário de CheckInMediador Eu zombo de todos os objetos com os quais ele fala.

EDITAR: Gishu é tecnicamente correto, nem tudo precisa ser ridicularizado, eu não zombo por exemplo CheckInInfo já que é simplesmente um contêiner de dados.No entanto, qualquer coisa que você possa ver como um serviço externo (e é quase tudo que transforma dados ou tem efeitos colaterais) deve ser ridicularizado.

Uma analogia que gosto é pensar em um design adequadamente acoplado como um campo com pessoas ao seu redor jogando uma partida de pega-pega.Quando a bola é passada para alguém, ele pode jogar uma bola completamente diferente para a próxima pessoa, ele pode até lançar várias bolas em sucessão para pessoas diferentes ou jogar uma bola e esperar para recebê-la de volta antes de jogá-la para outra pessoa.É um jogo estranho.

Agora, como treinador e gerente, é claro que você deseja verificar como sua equipe funciona como um todo, para ter prática de equipe (testes de integração), mas também fazer com que cada jogador pratique sozinho contra barreiras e máquinas de arremesso de bola (testes unitários com zombarias).A única peça que falta nesta imagem são as falsas expectativas e por isso temos as nossas bolas untadas com alcatrão preto para que manchem o batente quando o atingem.Cada backstop tem uma 'área alvo' que a pessoa está mirando e se no final de um treino não houver nenhuma marca preta dentro da área alvo você sabe que algo está errado e a pessoa precisa de sua técnica ajustada.

Realmente reserve um tempo para aprender direito, o dia em que entendi Mocks foi um grande momento de a-ha.Combine isso com uma inversão do contêiner de controle e nunca mais voltarei.

Por outro lado, um de nossos profissionais de TI acabou de chegar e me deu um laptop grátis!

Como alguém disse antes, se você zombar de tudo para isolar de forma mais granular do que a classe que está testando, você desiste de impor coesão no código que está em teste.

Lembre-se de que a zombaria tem uma vantagem fundamental: a verificação do comportamento.Isso é algo que os stubs não fornecem e é o outro motivo que torna o teste mais frágil (mas pode melhorar a cobertura do código).

As simulações foram inventadas em parte para Responda à pergunta:Como você faria testes unitários de objetos se eles não tivessem getters ou setters?

Nos dias de hoje, recomendado a prática é simular papéis e não objetos.Use Mocks como uma ferramenta de design para falar sobre colaboração e separação de responsabilidades, e não como “smart stubs”.

Objetos simulados são 1) frequentemente usados ​​como um meio de isolar o código em teste, MAS 2) como Keithb já apontou, são importantes para "concentre-se nas relações entre objetos colaboradores".Este artigo fornece alguns insights e história relacionada ao assunto: Design orientado para a responsabilidade com objetos simulados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top