unit-testing - unitarias - testing unitario software
¿Por qué crear objetos simulados? (7)
Cuando se realizan pruebas unitarias, cada prueba está diseñada para probar un solo objeto. Sin embargo, la mayoría de los objetos en un sistema tendrán otros objetos con los que interactúen. Los objetos simulados son implementaciones ficticias de estos otros objetos, que se utilizan para aislar el objeto bajo prueba.
El beneficio de esto es que cualquier prueba unitaria que falle generalmente aísla el problema al objeto bajo prueba. En algunos casos, el problema será con el objeto simulado, pero esos problemas deberían ser más simples de identificar y corregir.
También podría ser una idea escribir algunas pruebas unitarias simples para los objetos simulados.
Se usan comúnmente para crear una capa de acceso a datos simulada de forma que las pruebas unitarias se puedan ejecutar de forma aislada del almacén de datos.
Otros usos podrían ser burlarse de la interfaz de usuario al probar el objeto controlador en el patrón MVC. Esto permite una mejor prueba automatizada de los componentes de la interfaz de usuario que de alguna manera puede simular la interacción del usuario.
Un ejemplo:
public interface IPersonDAO
{
Person FindById(int id);
int Count();
}
public class MockPersonDAO : IPersonDAO
{
// public so the set of people can be loaded by the unit test
public Dictionary<int, Person> _DataStore;
public MockPersonDAO()
{
_DataStore = new Dictionary<int, Person>();
}
public Person FindById(int id)
{
return _DataStore[id];
}
public int Count()
{
return _DataStore.Count;
}
}
Durante una entrevista reciente, me preguntaron por qué uno querría crear objetos simulados. Mi respuesta fue algo así como: "Tome una base de datos: si está escribiendo código de prueba, es posible que no desee que la prueba se conecte en vivo a la base de datos de producción donde se realizarán las operaciones reales".
A juzgar por la respuesta, mi respuesta claramente no era lo que el entrevistador estaba buscando. ¿Cuál es una mejor respuesta?
Estas son algunas situaciones en las que la burla es indispensable :
- Cuando está probando la interacción de GUI
- Cuando prueba la aplicación web
- Cuando está probando el código que interactúa con el hardware
- Cuando pruebas aplicaciones heredadas
Iré en una dirección diferente aquí. Stubbing / Faking hace todas las cosas mencionadas anteriormente, pero tal vez los entrevistadores pensaban en burlas como un objeto falso que causa que la prueba pase o falle. Estoy basando esto en la terminología xUnit . Esto podría haber llevado a una discusión sobre las pruebas estatales frente a las pruebas de comportamiento / interacción .
La respuesta que pueden haber estado buscando es: que un objeto simulado sea diferente de un talón. Un stub emula una dependencia para el método bajo prueba. Un stub no debe hacer que falle una prueba. Un simulacro hace esto y también comprueba cómo y cuándo se llama. Los simulacros hacen que una prueba pase o falle según el comportamiento subyacente. Esto tiene el beneficio de ser menos dependiente de los datos durante una prueba, pero vincularlo más estrechamente con la implementación del método.
Por supuesto, esto es una especulación, es más probable que solo quisieran que describieras los beneficios del punteo y DI.
Los objetos / funciones simuladas también pueden ser útiles cuando se trabaja en equipo. Si está trabajando en una parte de la base de código que depende de una parte diferente de la base de código de la que algún otro es responsable, que todavía se está escribiendo o no se ha escrito aún, un objeto / función simulada es útil en ofreciéndole un resultado esperado para que pueda continuar con su trabajo sin que lo detengan esperando a que la otra persona termine su parte.
Para tomar un enfoque ligeramente diferente (como creo que los burlas han sido bien cubiertos arriba):
"Tome una base de datos: si está escribiendo un código de prueba, es posible que no desee que la prueba se conecte en vivo a la base de datos de producción donde se realizarán las operaciones reales".
OMI una mala forma de indicar el uso del ejemplo. Nunca lo "conectaría a la base de datos prod" durante la prueba con o sin simulacros. Todos los desarrolladores deben tener una base de datos de desarrollador local para contrastar. Y luego se movería en la base de datos de los entornos de prueba, tal vez UAT y finalmente prod. No se burla para evitar el uso de la base de datos en vivo, se burla para que las clases que no dependen directamente de una base de datos no requieran que configure una base de datos.
Finalmente (y acepto que podría obtener algunos comentarios al respecto) IMO, la base de datos local del desarrollador es válida para las pruebas unitarias. Solo debería estar tocándolo mientras prueba un código que interactúa directamente con la base de datos y usa simulaciones cuando prueba un código que accede indirectamente a la base de datos.
Resumiría así:
- Aislamiento : puede probar solo un método, independientemente de lo que llame. Su prueba se convierte en una prueba de unidad real (más importante en mi humilde opinión)
- Disminuya el tiempo de desarrollo de la prueba : por lo general, es más rápido usar una simulación que crear una clase completa solo para ayudarlo a evaluar
- Le permite probar incluso cuando no haya implementado todas las dependencias . Ni siquiera necesita crear, por ejemplo, su clase de repositorio, y podrá probar una clase que usaría este repositorio.
- Lo mantiene alejado de recursos externos : ayuda en el sentido de que no necesita acceder a bases de datos, llamar a servicios web, leer archivos, enviar correos electrónicos, cargar tarjetas de crédito, etc.
En una entrevista, recomendaría incluir que la burla es incluso mejor cuando los desarrolladores usan la inyección de dependencia , una vez que le permite tener más control y desarrollar pruebas más fácilmente.
Solo para agregar a las respuestas correctas aquí, los objetos simulados se utilizan en la programación estructural descendente o ascendente (OOP también). Están ahí para proporcionar datos a los módulos de nivel superior (GUI, procesamiento lógico) o para actuar como resultado de salida simulada.
Considere un enfoque de arriba hacia abajo: primero desarrolla una GUI, pero una GUI debe tener datos. Entonces creas una base de datos simulada que simplemente devuelve un std :: vector <> de datos. Usted ha definido el ''contrato'' de la relación. A quién le importa lo que sucede dentro del objeto de la base de datos, siempre y cuando mi lista GUI obtenga un std :: vector <> Estoy contento. Esto puede proporcionar información de inicio de sesión de usuario simulada, lo que sea que necesite para que la GUI funcione.
Considera un enfoque de abajo arriba. Usted escribió un analizador que lee en archivos de texto delimitados. ¿Cómo sabes si está funcionando? Usted escribe un ''receptor de datos'' simulado para esos objetos y enruta los datos allí para verificar (aunque generalmente) que los datos se leen correctamente. El módulo en el siguiente nivel puede requerir 2 fuentes de datos, pero solo ha escrito uno.
Y al definir los objetos simulados, también debe definir el contrato de la relación. Esto se usa a menudo en la programación basada en pruebas. Usted escribe los casos de prueba, utiliza los objetos simulados para hacerlo funcionar y, a menudo, la interfaz del objeto simulado se convierte en la interfaz final (por eso en algún momento puede desear separar la interfaz del objeto simulado en clase abstracta pura) .
Espero que esto ayude