Preciso bancar um advogado do diabo nessa questão, porque não posso defendê-la bem por falta de experiência. Aqui está o acordo, eu recebo conceitualmente as diferenças entre teste de unidade e teste de integração. Ao se concentrar especificamente nos métodos de persistência e no repositório, um teste de unidade usaria uma simulação, possivelmente por meio de uma estrutura como o Moq, para afirmar que o pedido pesquisado foi retornado conforme o esperado.
Digamos que eu criei o seguinte teste de unidade:
[TestMethod]
public void GetOrderByIDTest()
{
//Uses Moq for dependency for getting order to make sure
//ID I set up in 'Arrange' is same one returned to test in 'Assertion'
}
Portanto, se eu configurar OrderIdExpected = 5
e meu objeto simulado retornar 5
como o ID, meu teste passará. Entendi. Eu testei o código da unidade para garantir que o meu código pré-formado retorne o objeto e o ID esperados e não outra coisa.
O argumento que receberei é o seguinte:
"Por que não pular os testes de unidade e fazer testes de integração? É importante testar o procedimento armazenado do banco de dados e seu código. Parece muito trabalho extra ter testes de unidade e testes de integração quando, no final das contas, quero saber se o banco de dados chama e o código funciona. Sei que os testes levam mais tempo, mas precisam ser executados e testados independentemente, portanto, para mim parece inútil ter os dois. Apenas teste o que importa. "
Eu poderia defendê-lo com uma definição de livro de texto como: "Bem, isso é um teste de integração e precisamos testar o código separadamente como um teste de unidade e, yada, yada, yada ..." Esse é um caso em que uma explicação purista das práticas vs. a realidade está perdendo. Às vezes me deparo com isso e, se não consigo defender o raciocínio por trás do código de teste de unidade que, em última análise, depende de dependências externas, não posso argumentar.
Qualquer ajuda sobre esta questão é muito apreciada, obrigado!
fonte
Respostas:
Testes de unidade e testes de integração têm finalidades diferentes.
Os testes de unidade verificam a funcionalidade do seu código ... que você obtém o que espera do método quando o chama. Os testes de integração testam como o código se comporta quando combinado como um sistema. Você não espera que os testes de unidade avaliem o comportamento do sistema, nem que os testes de integração verifiquem saídas específicas de um método específico.
Os testes de unidade, quando feitos corretamente, são mais fáceis de configurar do que os testes de integração. Se você confiar apenas em testes de integração, seu teste será:
Conclusão: use o teste de integração para verificar se as conexões entre os objetos estão funcionando corretamente, mas use o teste de unidade primeiro para verificar os requisitos funcionais.
Tudo isso dito, talvez você não precise de testes de unidade para certos métodos de repositório. O teste de unidade não deve ser realizado em métodos triviais ; se tudo o que você está fazendo é passar por uma solicitação de um objeto para o código gerado pelo ORM e retornar um resultado, não é necessário fazer o teste de unidade na maioria dos casos; um teste de integração é adequado.
fonte
Eu estou do lado dos pragmáticos. Não teste seu código duas vezes.
Nós escrevemos apenas testes de integração para nossos repositórios. Esses testes dependem apenas de uma configuração de teste simples que é executada em um banco de dados na memória. Eu acho que eles fornecem tudo o que um teste de unidade faz e muito mais.
fonte
Os testes de unidade fornecem um nível de modularidade que os testes de integração (por projeto) não podem. Quando um sistema é reformulado ou recomposta, (e isso vai acontecer) testes de unidade pode muitas vezes ser reutilizado, enquanto os testes de integração deve muitas vezes ser re-escrita. Testes de integração que tentam fazer o trabalho de algo que deveria estar em um teste de unidade geralmente fazem muito, o que dificulta a manutenção.
Além disso, a inclusão de testes de unidade tem os seguintes benefícios:
É possível dividir seu trabalho (e aplicar uma abordagem DRY) enquanto você usa os testes de unidade e integração. Simplesmente confie nos testes de unidade para pequenas unidades de funcionalidade e não repita nenhuma lógica que já esteja em um teste de unidade em um teste de integração. Isso geralmente leva a menos trabalho (e, portanto, menos re-trabalho).
fonte
Os testes são úteis quando são interrompidos: proativamente ou reativamente.
Os testes de unidade são proativos e podem ser uma verificação funcional contínua, enquanto os testes de integração são reativos em dados faseados / falsos.
Se o sistema em consideração estiver próximo / dependente dos dados mais do que da lógica computacional, os Testes de Integração deverão ganhar mais importância.
Por exemplo, se estivermos construindo um sistema ETL, os testes mais relevantes serão em torno de dados (faseados, falsificados ou ativos). O mesmo sistema terá alguns testes de unidade, mas apenas em torno de validação, filtragem etc.
O padrão do repositório não deve ter lógica computacional ou comercial. É muito próximo ao banco de dados. Mas o repositório também está próximo da lógica de negócios que o utiliza. Portanto, é uma questão de encontrar um BALANÇO.
Os testes de unidade podem testar como o repositório se comporta. Os testes de integração podem testar o que realmente aconteceu quando o repositório foi chamado.
Agora, "O QUE REALMENTE ACONTECEU" pode parecer muito atraente. Mas isso pode ser muito caro para executar de forma consistente e repetida. A definição clássica de testes quebradiços se aplica aqui.
Portanto, na maioria das vezes, acho bom o suficiente escrever o teste de unidade em um objeto que usa o repositório. Dessa forma, testamos se o método de repositório apropriado foi chamado e os resultados simulados adequados são retornados.
fonte
Há três itens separados que precisam ser testados: o procedimento armazenado, o código que chama o procedimento armazenado (por exemplo, sua classe de repositório) e o consumidor. A tarefa do repositório é gerar uma consulta e converter o conjunto de dados retornado em um objeto de domínio. Há código suficiente para oferecer suporte ao teste de unidade que se separa da execução real da consulta e da criação do conjunto de dados.
Então (este é um exemplo muito, muito simplificado):
Em seguida, ao testar
OrderRepository
, passe um mockedISqlExecutor
e verifique se o objeto em teste passa no SQL correto (esse é o seu trabalho) e retorna umOrder
objeto apropriado, considerando alguns conjuntos de dados de resultados (também mocked). Provavelmente, a única maneira de testar aSqlExecutor
classe concreta é com testes de integração, justos o suficiente, mas essa é uma classe de invólucro fino e raramente muda, então é grande coisa.Você ainda precisa testar seus procedimentos armazenados também.
fonte
Posso reduzir isso para uma linha de pensamento muito simples: favorecer os testes de unidade porque ajuda a localizar bugs. E faça isso de forma consistente, porque inconsistências causam confusão.
Outras razões derivam das mesmas regras que orientam o desenvolvimento de OO, uma vez que também se aplicam a testes. Por exemplo, o princípio da responsabilidade única.
Se alguns testes de unidade parecem que não valem a pena, talvez seja um sinal de que o assunto não vale a pena. Ou talvez sua funcionalidade seja mais abstrata que sua contraparte e os testes para ela (assim como seu código) possam ser abstraídos para um nível superior.
Como com qualquer coisa, há exceções. E a programação ainda é uma forma de arte; muitos problemas são diferentes o suficiente para justificar a avaliação de cada um para obter a melhor abordagem.
fonte