¿La inyección de dependencia es esencial para las pruebas unitarias?

56

¿Es esencial el uso de la inyección de dependencia (DI) para las pruebas unitarias?

No puedo pensar en otra alternativa para aislar el código para que pueda probarse. Además, todos los ejemplos que he visto usan este patrón. ¿Es porque es la única opción viable o hay otras alternativas?

Tom Squires
fuente
55
La inyección de dependencia no es esencial, pero el concepto más amplio de inversión de control sí lo es.
Jeremy Heiler
Hay algo que decir sobre la escala aquí. Si tengo una pequeña base de código con muy pocas capas, entonces DI puede no ser útil.
JB King
@JBKing si tienes una pequeña base de código no necesitas capas o pruebas unitarias
Sklivvz
1
Son necesarias muchas decisiones de diseño para que su código sea comprobable. Comienza a escribir pruebas y descúbrelo.
Nathan Cooper

Respuestas:

43

DI facilita mucho las pruebas unitarias. Pero aún puede escribir pruebas unitarias sin DI. Ya se han escrito muchas pruebas unitarias antes de que la DI se generalizara. (Por supuesto, algunas de estas técnicas utilizadas son idénticas o muy similares a la DI sin saber que tiene un nombre elegante :-)

Yo mismo he usado mucho, por ejemplo, interfaces y fábricas antes de aprender sobre DI. El nombre real de la clase de fábrica puede haberse leído de un archivo de configuración, o pasado al SUT como argumento.

Otro enfoque es usar singletons (o datos accesibles a nivel mundial en general). Sí, sé que no es recomendado por muchos (incluido yo mismo) en general. Aún así, puede ser viable en situaciones específicas, especialmente si el singleton contiene datos de configuración estáticos que no son específicos del caso de prueba, pero difieren entre la producción y el entorno de prueba. Por supuesto, tiene sus problemas conocidos, por lo que la DI es superior si puede usarla. Pero a menudo (por ejemplo, en sistemas heredados) no se puede.

Hablando de eso, Trabajar eficazmente con código heredado describe muchos trucos para obtener código heredado cubierto por pruebas. Muchos de estos no son agradables y no se consideran una solución a largo plazo. Pero le permiten crear las primeras pruebas unitarias valiosas para un sistema que de otro modo no sería comprobable ... lo que le permite comenzar a refactorizar, y eventualmente (entre otros) introducir DI.

Péter Török
fuente
De acuerdo, DI es particularmente útil para burlarse de objetos. Pero hay muchos escenarios en los que la DI es inútil en las pruebas.
Kemoda
@Kemoda, por ejemplo, ¿qué?
Bћовић
@VJovic Por ejemplo, objetos independientes. Pienso en un método que toma algunos argumentos y realiza algunas cosas, pero no depende de otro componente (por lo tanto, no es necesario DI).
Kemoda
@Kemoda Parece que estás describiendo una programación funcional y estás usando DI. Estás inyectando tus dependencias como parámetros de método.
Erik Dietrich
3
@huggie, ¿por qué se filtrarían los detalles de implementación aquí? La dependencia inyectada generalmente se oculta detrás de una interfaz, y el punto es que la clase cliente no tiene idea, y no le preocupa, si la implementación real de esta dependencia es una clase de producción real o una simulación. Su instanciación ocurre fuera de la clase de cliente, solo puede ver la instancia preparada.
Péter Török
56

El desacoplamiento es esencial para las pruebas unitarias. DI es una excelente manera de lograr el desacoplamiento.

nlawalker
fuente
17
Una afirmación muy verdadera que de ninguna manera responde a la pregunta.
Travis
10

Dependiendo de las tecnologías que esté utilizando, puede aislar dependencias sin usar DI. Por ejemplo, en el mundo .NET, Moles le permite aislar dependencias sin el patrón DI.

Dicho esto, creo que estos marcos de aislamiento están escritos y destinados a situaciones en su código con dependencias externas (sistema de archivos, base de datos, etc.). Es decir, el hecho de que uno pueda hacer esto no significa que deba hacerlo.

La inyección de dependencia permite pruebas unitarias, pero también permite modificar el comportamiento de un objeto sin alterar el código de ese objeto (principio abierto / cerrado). Por lo tanto, no se trata solo de un código comprobable, sino de un código flexible que resulta. En general, he descubierto que existe una gran correlación entre el código mantenible / flexible y el código comprobable.

Erik Dietrich
fuente
3
O Moles te permite pensar que tienes un código comprobable limpio y bien factorizado porque estás probando con magia.
Wyatt Barnett
@WyattBarnett Sí, muy cierto. Los lunares tienen una habilidad que induce a hacer una mueca para que alguien diga "¿quién necesita todo este polimorfismo y cosas abiertas / cerradas, de todos modos?!?"
Erik Dietrich
1
Moles fue reemplazado por falsificaciones , y desde la página de falsificaciones "El marco Fakes ayuda a los desarrolladores a crear, mantener e inyectar implementaciones ficticias en sus pruebas unitarias". Me parece DI.
Firmar el
1
@Sign Su enlace también dice: "El marco Fakes se puede utilizar para calzar cualquier método .NET, incluidos los métodos no virtuales y estáticos en tipos sellados". El hecho de que los marcos de aislamiento se puedan usar junto con DI no significa que sean DI.
Erik Dietrich
4

No, la DI no es esencial para las pruebas unitarias, pero ayuda mucho.

Puede usar fábricas o localizadores y probar como lo haría con DI (solo que no es tan elegante y requeriría más configuración).

Además, Mock Objects sería importante en sistemas heredados donde muchas llamadas se delegan a funciones en lugar de dependencias. (Los objetos simulados también se pueden utilizar ampliamente en una configuración adecuada)

Puede haber configuraciones donde la prueba es casi imposible. Pero esto no se basa en si se usa o no la inyección de dependencia.

smp7d
fuente
3

No , la inyección de dependencia no es esencial para las pruebas unitarias.

La inyección de dependencia ayuda si tiene una clase que necesita una instancia de clase dependiente para realizar un subprocesamiento. En lugar de DI, puede separar la lógica de un método comercial en una parte de recopilación de datos (que no es comprobable por unidad) y una parte de cálculo que puede probarse por unidad.

Ejemplo (usando DI) Esta implementación depende de Empleado, Cuenta, ...

 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;
     ...
 }

Después de la separación de la recopilación de datos y el cálculo:

 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
     ...
 }

La parte de cálculo se puede probar fácilmente sin inyección de dependencia.

k3b
fuente
1
  • La inyección de dependencia no es esencial para las pruebas unitarias

  • La inversión del control, por otro lado, es esencial cuando desea intercambiar una implementación por otra.

CodeART
fuente
0

Sí, hay alternativas al uso de DI para el aislamiento.

Una alternativa es utilizar fábricas o un ServiceLocator que se puede configurar a partir de pruebas para devolver objetos simulados en lugar de objetos reales.

Otra es utilizar un marco de aislamiento adecuado o una herramienta de burla. Dichas herramientas existen para casi todos los lenguajes de programación modernos (para Java, C #, Ruby y Python, al menos). Pueden aislar una clase que se está probando de la implementación de otras clases / tipos, incluso cuando la clase probada instancia directamente sus dependencias.

Rogério
fuente