A injeção de dependência é essencial para o teste de unidade?

56

O uso de injeção de dependência (DI) é essencial para o teste de unidade?

Não consigo pensar em outra alternativa para isolar o código para que possa ser testado. Além disso, todos os exemplos que eu já vi usam esse padrão. Isso é porque é a única opção viável ou existem outras alternativas?

Tom Squires
fonte
5
Injeção de Dependência não é essencial, mas o conceito mais amplo de Inversão de Controle é.
21412 Jeremy Heiler
Há algo a ser dito para a escala aqui. Se eu tiver uma pequena base de código com muito poucas camadas, o DI pode não ser útil.
JB King
@JBKing se você tem uma pequena base de código que você não precisa camadas ou teste de unidade
Sklivvz
11
Muitas decisões de design são necessárias para tornar seu código testável. Comece a escrever testes e descubra.
Nathan Cooper

Respostas:

43

O DI facilita muito o teste de unidade. Mas você ainda pode escrever testes de unidade sem DI. Muitos testes de unidade já foram escritos antes do DI se espalhar. (Obviamente, algumas dessas técnicas usadas são idênticas ou muito semelhantes ao DI sem saber que ele tem um nome sofisticado :-)

Eu mesmo já usei muito interfaces e fábricas antes de aprender sobre DI. O nome da classe de fábrica real pode ter sido lido de um arquivo de configuração ou passado para o SUT como argumento.

Outra abordagem é usar singletons (ou dados acessíveis globalmente em geral). Sim, eu sei que isso não é recomendado por muitos (inclusive eu) em geral. Ainda assim, pode ser viável em situações específicas, especialmente se o singleton contiver dados de configuração estática que não são específicos de caso de teste, mas diferem entre produção e ambiente de teste. É claro que ele tem problemas conhecidos, então o DI é superior se você puder usá-lo. Mas muitas vezes (por exemplo, em sistemas legados) você não pode.

Falando nisso, Trabalhando efetivamente com o código herdado descreve muitos truques para obter o código herdado coberto pelos testes. Muitos deles não são bons e não são uma solução a longo prazo. Mas eles permitem que você crie os primeiros testes de unidade valiosos em um sistema não testável ... que permite iniciar a refatoração e, eventualmente (entre outros), introduzir a DI.

Péter Török
fonte
Concordado, o DI é particularmente útil para zombar de objetos. Mas há muitos cenários em que o DI é inútil nos testes.
Kemoda
@Koda como, por exemplo, o que?
22223
@VJovic Por exemplo, objetos independentes. Penso em um método que leva alguns argumentos e executa algumas coisas, mas não depende de outro componente (portanto, não há necessidade de DI).
Kemoda
@Kemoda Parece que você está descrevendo a programação funcional e está usando o DI. Você está injetando suas dependências como parâmetros de método.
Erik Dietrich
3
@ huggie, por que os detalhes da implementação vazaram aqui? A dependência injetada geralmente está oculta por trás de uma interface, e o ponto principal é que a classe cliente não tem idéia - e não está preocupada - se a implementação real dessa dependência é uma classe de produção real ou uma farsa. Sua instanciação acontece fora da classe do cliente, ela apenas vê a instância pronta.
Péter Török
56

A dissociação é essencial para testes de unidade. A DI é uma ótima maneira de conseguir a dissociação.

nlawalker
fonte
17
Uma afirmação muito verdadeira que de forma alguma responde à pergunta.
Travis
10

Dependendo das tecnologias que você está usando, você pode isolar dependências sem usar o DI. Por exemplo, no mundo .NET, o Moles permite isolar dependências sem o padrão DI.

Dito isso, acredito que essas estruturas de isolamento sejam escritas e destinadas a situações em seu código com dependências externas (sistema de arquivos, banco de dados etc.). Ou seja, o fato de alguém poder fazer isso não significa que ele ou ela deva.

A injeção de dependência permite o teste de unidade, mas também permite a modificação do comportamento de um objeto sem alterar o código desse objeto (princípio de aberto / fechado). Portanto, não é apenas um código testável, mas um código flexível que resulta. Geralmente descobri que há uma forte correlação entre código sustentável / flexível e código testável.

Erik Dietrich
fonte
3
Ou Moles permite que você pense que possui um código testável limpo e bem fatorado porque está testando através da magia.
Wyatt Barnett
@ WyattBarnett Sim, é verdade. Moles tem a capacidade de induzir alguém a dizer "quem precisa de todo esse polimorfismo e coisas abertas / fechadas, afinal?!?"
Erik Dietrich
11
Moles foi substituído por fakes e, na página de fakes "A estrutura do Fakes ajuda os desenvolvedores a criar, manter e injetar implementações fictícias em seus testes de unidade" Soa como DI para mim.
Assine
11
@Sign Seu link também diz: "A estrutura do Fakes pode ser usada para ajustar qualquer método .NET, incluindo métodos não virtuais e estáticos em tipos selados". O fato de as estruturas de isolamento poderem ser usadas em conjunto com o DI não significa que elas sejam DI.
Erik Dietrich
4

Não, o DI não é essencial para testes de unidade, mas ajuda muito.

Você pode usar fábricas ou localizadores e testar como faria com o DI (apenas não tão elegante e exigiria mais configurações).

Além disso, o Mock Objects seria importante em sistemas legados, onde muitas chamadas são delegadas a funções, em vez de dependências. (Objetos simulados também podem ser amplamente utilizados em uma configuração adequada)

Pode haver configurações em que o teste é quase impossível. Mas isso não se baseia no uso ou não da injeção de dependência.

smp7d
fonte
3

Não , a injeção de dependência não é essencial para o teste de unidade.

A injeção de dependência ajuda se você tiver uma classe que precisa de uma instância de classe dependente para realizar algum subprocessamento. Em vez de DI, você pode separar a lógica de um método comercial em uma parte de coleta de dados (que não pode ser testada por unidade) e uma parte de cálculo que pode ser testada por unidade.

Exemplo (usando DI) Esta implementação depende de Funcionário, Conta, ...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

Após a separação da coleta e cálculo dos dados:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

A parte do cálculo pode ser facilmente testada sem injeção de dependência.

k3b
fonte
1
  • A injeção de dependência não é essencial para testes de unidade

  • A inversão do controle, por outro lado, é essencial quando você deseja trocar uma implementação por outra.

CodeART
fonte
0

Sim, existem alternativas ao uso de DI para isolamento.

Uma alternativa é usar fábricas ou um ServiceLocator que pode ser configurado a partir de testes para retornar objetos simulados em vez de reais.

Outra é usar uma estrutura de isolamento ou ferramenta de zombaria adequada. Essas ferramentas existem para praticamente todas as linguagens de programação modernas (para Java, C #, Ruby e Python, pelo menos). Eles podem isolar uma classe que está sendo testada da implementação de outras classes / tipos, mesmo quando a classe testada instancia diretamente suas dependências.

Rogério
fonte