Table of Contents
Introducción a objetos de la cubierta en TDD
El desarrollo de pruebas (TDD) es una piedra angular de pruebas de software de ingeniería moderna, promoviendo la fiabilidad de código, la mantenibilidad y un bucle de retroalimentación de diseño claro. En TDD, los desarrolladores escriben primero una prueba de falla, luego producen suficiente código de producción para pasar esa prueba, y finalmente refactor. Para aislar la unidad bajo prueba de dependencias externas como bases de datos, servicios web o sistemas de archivos, objetos de mock se hace indispensable.
Cuando se hace correctamente, la burla ayuda a identificar fallas de diseño temprano, impone la inversión de dependencia y produce pruebas rápidas y fiables. Sin embargo, las medias mal elaboradas conducen a suites de prueba frágiles y difíciles de mantener que ocultan errores en lugar de revelarlos. Este artículo explora las mejores prácticas para escribir objetos de mock en el contexto de TDD, con orientación práctica para equipos de ingeniería que buscan mejorar sus prácticas de prueba.
Comprender los objetos de la cubierta y su papel
Antes de sumergirse en las mejores prácticas, es importante aclarar la terminología. Aunque a menudo se utilizan intercambiablemente, los dobles de prueba caen en varias categorías, cada una con un propósito distinto. El artículo clásico de Martin Fowler "Las cosas no son estubos"] proporciona una taxonomía fundamental:
- Dummy – Un objeto que se pasa alrededor pero nunca se utiliza, normalmente para satisfacer las firmas de los métodos.
- Stub – Proporciona respuestas enlatadas a las llamadas hechas durante la prueba, a menudo usadas para controlar los insumos indirectos.
- Spy – Registros de información sobre cómo se llamaba, permitiendo una verificación posterior.
- Mock – Preprogramado con expectativas sobre qué llamadas deben hacerse y cuántas veces; afirma que la interacción ocurrió como se esperaba.
- Fake] – Una implementación de trabajo ligera (por ejemplo, una base de datos en memoria) que no es adecuada para la producción sino útil para la prueba.
En TDD estricto, las medias y los espías son las herramientas primarias para las pruebas basadas en la interacción, mientras que los obstáculos apoyan las pruebas basadas en el estado. Entendiendo estas distinciones ayuda a los ingenieros a elegir el doble de prueba adecuado para cada escenario.
Los marcos de burla modernos (por ejemplo, Mockito, Jest, unittest.mock) difuminan estas líneas ofreciendo características combinadas, pero la claridad conceptual sigue siendo crítica. Un objeto de mock en TDD debe verificar que el sistema bajo prueba (SUT) interactúa con sus dependencias de la manera esperada, escalando métodos específicos con argumentos correctos y respetando el orden o la frecuencia de llamada.
Prácticas básicas para escribir objetos de la burla
Las siguientes prácticas se destilan de años de experiencia en la industria y sabiduría comunitaria. Adherirse a ellas hará que sus pruebas sean más fiables, legibles y resistentes a la refactorización.
1. Mantener los Mocks Simple y Centrado
Diseñar cada mock para simular sólo el comportamiento exacto requerido por la prueba. Evite sobrecargar mocks con problemas innecesarios, valores de retorno o verificaciones. Cuando una mock hace demasiado, la intención de la prueba se obsesiona, y los costos de mantenimiento aumentan. Por ejemplo, si el SUT sólo llama un método de repositorio , el mock no debe definir el comportamiento para el mismo método de configuración que el ejercicio
Además, prefiere utilizar respuestas predeterminadas o mocks de lentejas (donde el marco permite) evitar las pruebas de ruptura cuando el SUT evoluciona. En Mockito, evita errores innecesarios cuando no se llaman métodos obstruidos; en Jest, ] devuelve por defecto. Esto mantiene pruebas centradas en la interacción que importa.
2. Uso de Convenios de Naming claros
El nombre de una variable de mock debe comunicar su papel y la dependencia que reemplaza. En lugar de o , utilizar nombres descriptivos como o . Esto es especialmente importante en las grandes suites de prueba donde los desarrolladores escanean rápidamente código de configuración.
Para los métodos de mock, si crea implementaciones de mock personalizadas (que se necesitan con marcos), utilice nombres de método que indican claramente el comportamiento simulado, como o . Evite nombres genéricos como que ocultan los detalles.
3. Verificar las interacciones expeditivamente
El propósito principal de una moca es afirmar que se produjeron interacciones particulares. Use las características de verificación de su marco de burla para confirmar que métodos específicos fueron llamados con argumentos esperados, conteo de llamadas o orden. Por ejemplo, en Mockito:
Mockito.verify(mockService, times(1)).processPayment(anyString(), eq(100.0));
En Jest:
expect(mockService.processPayment).toHaveBeenCalledTimes(1);
expect(mockService.processPayment).toHaveBeenCalledWith('order-123', 100.0);
Tenga cuidado de verificar sólo lo que es esencial para el contrato conductual. La verificación excesiva (por ejemplo, comprobar que no se llamaron otros métodos a través ] indiscriminadamente) puede hacer pruebas frágiles. Reserve tal verificación estricta para escenarios donde los efectos secundarios no deseados son una preocupación real.
4. Evitar el uso excesivo de mocks
El movimiento no es una opción predeterminada. El exceso de movimiento conduce a pruebas que están estrechamente acopladas a los detalles de la implementación, haciendo que la refactorización sea dolorosa.
- Mock only external boundaries – Dependencias que cruzan procesos, redes o límites I/O (por ejemplo, un cliente de base, una API REST, un sistema de archivos).
- Preferir objetos reales para los trabajadores de los col en proceso – Si un colaborador es simple, rápido y sin efectos secundarios (por ejemplo, un objeto de valor o clase de utilidad), utilizarlo directamente en lugar de burlarse de él.
- Evitar los tipos de burla que posees] – Si controlas la implementación de una dependencia, considera si una versión falsa (una versión de memoria ligera) sería más mantenible que una burla con decenas de problemas.
- Use tests de integración para flujos de trabajo complejos] – Mientras que las medias son excelentes para pruebas unitarias, pruebas de integración (utilizando dependencias reales o containerizzate) capturan errores de coordinación que las medias no pueden.
Una buena regla de pulgar: si te encuentras escribiendo 20 líneas de configuración de mock para una sola prueba de unidad, puede ser un signo de que el SUT tiene demasiadas dependencias o que deberías considerar un enfoque de prueba diferente.
5. Dependencias inyectables
Los objetos de mock solo funcionan cuando el SUT acepta sus dependencias a través de la inyección de constructores, parámetros de método o (menos idealmente) inyección de setter. Los métodos estaticos, el estado global y la creación de objetos dentro del SUT (utilizando ) están burlando anti-patterns. Escriba su código de producción con la inyección de dependencia (DI) en mente.
public class OrderService {
private final PaymentGateway paymentGateway;
public OrderService(PaymentGateway paymentGateway) {
this.paymentGateway = paymentGateway;
}
// ...
}
Este diseño permite que las pruebas sustituyan una mock fácilmente. Si su base de código utiliza un contenedor DI, asegúrese de que la configuración de prueba puede anular las implementaciones reales con mocks.
6. Use Datos realistas y de código de bordes
Los mocks deben devolver datos que reflejen los valores de producción, incluyendo tipos, rangos y estructuras. Evite usar valores triviales de marcadores de posición como cadenas vacías o 0 para cada prueba a menos que sea el escenario que se está probando. Use cargas de pago realistas para descubrir desprendimientos temprano. Por ejemplo, si un método espera una lista de pedidos, devuelva una lista con varios elementos, no una lista vacía, a menos que la prueba explícitamente cubre el caso vacío.
Un error común es burlar un repositorio para siempre devolver un objeto cuando la aplicación real puede volver o lanzar una excepción. Los exámenes luego pasan, pero el código de producción falla. Use su mock para simular tanto el éxito como las rutas de fracaso sistemáticamente.
7. Reiniciar los mocks entre los exámenes
En cualquier suite de prueba, las mocks deben ser frescos para cada caso de prueba para prevenir fugas estatales. La mayoría de los marcos modernos ofrecen anotaciones o métodos de configuración para restablecer mocks automáticamente. En JUnit 5 con Mockito, use y anotaciones: las mocas se reinician por prueba. En Jest, use en un bloque [[FLT22 mucaja.
Herramientas y marcos para la manipulación
La selección de la herramienta correcta de burla simplifica la implementación de las mejores prácticas. A continuación se presentan los principales marcos en todos los idiomas populares, junto con la orientación sobre el uso eficaz.
Java: Mockito
Mockito] es el estándar de facto para las pruebas de unidad Java. Soporta la creación de mock impulsada por la anotación, los emparejadores de argumentos flexibles y una API de verificación limpia. Use y para reducir el calderado. Evite el por defecto; fomenta pruebas frágiles.
JavaScript/TypeScript: Jest
Jest viene con la burla integrada a través de , , y . Se burla automáticamente de los módulos cuando se utiliza . Para los mocos manuales, crear directorios. Una mejor práctica es usar para conseguir un mock de inicio y luego anular comportamientos indiscriminados.
Python: unidad de prueba.mock
.NET: Moq
Moq es la biblioteca de burlas más popular para .NET, utilizando una interfaz fluida. Ejemplo: . Moq soporta comportamiento de burla estricto y suelto; comienza con suelto (default) y apretar sólo cuando sea necesario. Use para pruebas de interacción.
Ruby: ¿Mocks RSpec
La manipulación integrada de RSpec es compatible , ] (que verifica la conformidad de la interfaz), y . Use para los problemas y para las verificaciones. Los dobles verificados (usando nombres de clase) capturan desdibujos de interfaz en el tiempo de prueba.
Pitfalls comunes y cómo evitarlos
Incluso los desarrolladores experimentados caen en trampas cuando usan objetos de mock. La conciencia es el primer paso hacia la mitigación.
Mocking Todo en la vista
Esto lleva a pruebas que son de caja blanca, frágiles y lentas para escribir. En lugar de ello, burlarse sólo en los límites arquitectónicos (por ejemplo, I/O, servicios de terceros). Para la lógica interna, utilizar objetos reales.
Utilizando valores de retorno de cocido duro sin consideración
Volver o sin que coincida con formatos reales puede ocultar errores de tipo o formato. Generar datos de prueba realistas utilizando fábricas, bibliotecas de falsos o archivos de fijación mínima.
Orden de llamada de alta velocidad o cuenta
A menos que el pedido de llamada sea un requisito crítico (por ejemplo, un flujo de trabajo de pago debe validar antes de la carga), use verificaciones espaciosamente. Asimismo, es a menudo el predeterminado y puede omitirse; sólo especificar el recuento exacto cuando se divierte.
Desvelado para verificar caminos excepcionales
El código de producción debe manejar fallos. Use mocks para lanzar excepciones y verifique que el SUT reacciona correctamente (por ejemplo, registros, retries, devuelve el descuido). Sin esto, las pruebas proporcionan falsa confianza.
Técnicas avanzadas
Una vez que dominas los conceptos básicos, considera estas técnicas para manejar escenarios de pruebas más complejos.
Mocks parciales (Spies)
A veces necesitas probar un objeto real pero tejer un solo método. Marcos como Mockito permiten crear un espía en un caso real: . Usar este método espaciantemente, mezcla el comportamiento real y simulado, que puede confundir la intención de prueba.
Usando Argument Matchers Pensadamente
Los emparejadores de argumentación (por ejemplo, ], ]) hacen que los mocos sean flexibles. Sin embargo, sean precisos: use sólo cuando el argumento exacto no afecte el resultado de la prueba. Cuando el argumento es crítico, lo captura con un y lo afirman por separado en sus propiedades.
Strict vs. Lenient Mocks
Las mocas estrictas fallan si se llama un método inesperado; las mocas de indulgencia ignoran llamadas no configuradas. El lentejuela es generalmente más resistente, especialmente durante la refactorización. Si adoptas un burlamiento estricto (por ejemplo, los estubbings estrictos de Mockito), prepárate para actualizaciones frecuentes de pruebas.
Integración con CI/CD y Contenedores de Prueba
Los objetos de la cubierta brillan en pruebas unitarias, pero tienen limitaciones. Para la verificación de interacciones con sistemas externos (por ejemplo, bases de datos, corredores de mensajes), considere utilizar contenedores de prueba] (por ejemplo, Testcontainers for Java, Testcontainers for .NET) junto con mocks a niveles de prueba más altos.
En un oleoducto de CI, realizar pruebas de unidad (con mocks) en cada commit; realizar pruebas de integración (con contenedores de prueba) en combinar solicitudes o construcciones programadas. Esto evita que las pruebas de integración lentas obstruyan la iteración del desarrollador al capturar errores de integración reales antes de la liberación.
Conclusión
Los objetos de mock son una herramienta esencial en el arsenal del practicante TDD, permitiendo pruebas unitarias aisladas, deterministas y rápidas. Las mejores prácticas descritas en este artículo —mantener las medias simples, nombrarlas claramente, verificar las interacciones explícitamente, evitar el uso excesivo e inyectar dependencias— constituyen una base sólida para crear suites de prueba sostenibles. Al elegir el marco de burla adecuado, evitar las dificultades comunes, e integrar las burlas con estrategias de mayor calidad, los equipos de ingeniería
Recuerde que la burla es un medio para un fin, no un fin en sí mismo. El objetivo final es impulsar el diseño a través de interfaces probables y producir software que se comporta correctamente bajo cada condición esperada, incluyendo errores y casos de borde. Evaluar continuamente sus prácticas de burla contra la retroalimentación real del proyecto, y adaptarse a medida que su base de código evoluciona.
Para más lectura, explore la documentación oficial de su marco elegido, y vuelva a revisar la taxonomía de Fowler regularmente para mantener su modelo mental afilado.