No teste de unidade, por que eu criaria um repositório duas vezes?

10

No outro dia, eu estava lendo um pouco sobre o Teste de Unidade e vi alguns exemplos em que as pessoas criam uma interface de repositório (ie IExampleRepository) e depois criam o repositório real ( public class ExampleRepository : IExampleRepository) e um repositório a ser usado para o teste de unidade ( FakeExampleRepository : IExampleRepository).

No IExampleRepositoryeles estavam implementando os mesmos métodos como no ExampleRepository, no entanto, com diferentes consultas Linq.

Qual é exatamente o objetivo aqui? Eu pensei que uma parte da unidade testando seu código é garantir que um método funcione corretamente? Mas quando eu uso duas consultas totalmente diferentes, uma para 'real' e outra no teste, qual o sentido do teste?

jao
fonte

Respostas:

8

Um dos objetivos do teste de unidade é testar apenas uma coisa de cada vez, ou seja, uma única classe e método. Se o repositório em si não estiver sendo testado, você normalmente zombaria disso de alguma forma, para testar a lógica em seu método / classe.

Dito isto, você também precisa testar com um repositório 'real' *, mas isso normalmente seria feito em um teste de integração / sistema

* obviamente real como no repo configurado para teste, espero que não seja, por exemplo, o DB de produção.

jk.
fonte
Portanto, no teste de unidade, não testarei o próprio método para garantir que ele retorne os valores corretos (com base em um conjunto de dados falso / falso)?
jao
sim, você testará o método em si, mas apenas o código no método, o código em outros objetos deve ser abordado em outros testes de unidade, a interação de vários objetos deve ser abordada em testes de integração ou em testes de nível superior
jk.
11
Ok, então se eu entendi corretamente, devo usar o repositório original ao testar a unidade. Eu não preciso de testá-los quando estou escrevendo testes de unidade para os controladores (no caso de Asp.Net MVC)
jao
4
@ Theomax Depende do contexto: se você estiver testando um componente de software que não é seu ExampleRepository, é melhor usar uma simulação. A justificativa é que você não está testando o repositório da unidade, mas outra coisa.
8133 Andres F.
5
@ Theomax Para expandir o comentário de AndresF.: Se você estiver testando uma unidade ExampleRepository, use a coisa real. Se você estiver testando a unidade RepositoryController, deve usar apenas um FakeExampleRepositoryque retorne valores pré-especificados. Dessa forma, se um bug aparecer ExampleRepository, apenas esse teste de unidade falhará - RepositoryControlleros testes continuarão bem-sucedidos, então você sabe que não há um bug lá. Se o controlador utilizado o repositório real, eles dois estariam falhando e você não saberia se você tivesse um bug ou 2.
Izkata
5

Eu concordo com as duas respostas de jk. e Jan Hudec - eles fornecem informações realmente boas. Mas pensei em acrescentar um pouco.

Sua primeira pergunta ("Qual é exatamente o objetivo aqui?") É importante. No caso que você está descrevendo, o objetivo real é testar as classes que estão utilizando a IExampleRepositoryinterface, não testando as implementações do repositório. Criar o FakeExampleRepositorypermite testar essas classes de clientes sem se preocupar com os detalhes da classe de repositório real.

Isso é especialmente verdadeiro se o objeto que você está tentando configurar dificultar o teste (por exemplo, acessa o sistema de arquivos, liga para um serviço da web ou conversa com um banco de dados). Usando interfaces (e outras técnicas), você mantém o acoplamento baixo. Portanto, a classe Xprecisa conhecer apenas a interface e os detalhes da implementação. O objetivo é garantir que a classe Xesteja fazendo a coisa certa.

Mocking (ou stubbing, fingindo ... existem diferenças sutis) é uma ferramenta poderosa para testes de unidade e TDD. Mas pode ser difícil criar e manter manualmente essas implementações. Portanto, a maioria dos idiomas agora tem bibliotecas de simulação para ajudar. Como você está usando C #, eu recomendaria o Moq porque é simples e muito poderoso. Em seguida, você pode testar a interface sem acumular código extra para as implementações simuladas.

Allan
fonte
the real objective is to test the classes that are utilizing the IExampleRepository interfaceisso não é estritamente verdade. O objetivo é testá-lo independentemente do IExampleRepository. 1 por recomendar uma boa estrutura de isolamento.
StuperUser
11
Discordo. Não é independente disso IExampleRepositoryporque a classe em teste está acoplada a essa interface. Mas é independente de quaisquer implementações da interface. Admito que minha explicação provavelmente poderia usar um pouco mais de delicadeza. :)
Allan
5

Qual é exatamente o objetivo aqui?

Isolamento.

A idéia de uma unidade de teste para testar a menor unidade de código possível . Você faz isso isolando -o de todos os outros códigos de produção no teste.

Ao criar classes falsas, o único código de produção é a classe em teste.

Se você criar um repositório falso corretamente e o teste falhar, você saberá que o problema está no código em teste. Isso oferece o benefício do diagnóstico gratuitamente.

Dê uma olhada nas estruturas de isolamento (como o Moq, como sugerido por @Allan), para poder gerar essas falsificações rapidamente para configurar as condições de teste e usá-las para reivindicar.

StuperUser
fonte
Mais detalhes sobre falsificações, zombarias e stubs: stackoverflow.com/questions/346372/…
StuperUser
4

Há três razões pelas quais você pode querer fornecer uma instância simulada ao teste de unidade:

  1. Você deseja limitar o escopo do teste, para que o teste não seja afetado por erros no depndee, possivelmente porque ainda não foi concluído ou não é estável ou você não deseja que os erros afetem seus testes.
  2. O dependente é complicado de configurar. Por exemplo, a camada de acesso a dados geralmente é ridicularizada, porque a real requer a criação de um banco de dados de teste. Você ainda precisa testar a camada de acesso a dados reais, mas pode limitar a configuração dispendiosa ao depurar outras coisas.
  3. Para testar se a classe dependente reage adequadamente a vários tipos de erros, você fornece uma versão simulada que retorna todos os tipos de respostas erradas. Porque muitos modos de falha são bastante difíceis de reproduzir, mas ainda devem ser testados.
Jan Hudec
fonte