unit-testing - test - pruebas unitarias php
¿Por qué es tan malo simular clases? (8)
Recientemente discutí con un colega sobre la burla. Dijo que las clases de burlas son muy malas y no deberían hacerse, solo en unos pocos casos.
Él dice que solo las interfaces deben ser burladas, de lo contrario es un error de arquitectura.
Me pregunto por qué esta afirmación (confío totalmente en él) es tan correcta. No lo sé y me gustaría estar convencido.
Perdí el punto de burla (sí, leí el artículo de Martin Fowler )
Editar: Como has aclarado que tu colega quería decir que la clase simulada es mala pero la interfaz falsa no, la respuesta a continuación no está actualizada. Deberías consultar esta respuesta .
Estoy hablando de simulacro y talón como lo define Martin Fowler , y supongo que eso es lo que su colega también quiso decir.
Burlarse es malo porque puede conducir a una sobreespecificación de las pruebas. Use stub si es posible y evite el simulacro.
Aquí está la diferencia entre el simulacro y el talón (del artículo anterior):
Entonces podemos usar la verificación de estado en el código auxiliar de esta manera.
class OrderStateTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); MailServiceStub mailer = new MailServiceStub(); order.setMailer(mailer); order.fill(warehouse); assertEquals(1, mailer.numberSent()); }
Por supuesto, esta es una prueba muy simple, solo que se envió un mensaje. No hemos probado que fue enviado a la persona adecuada, o con los contenidos correctos, pero servirá para ilustrar el punto.
Usar burlas esta prueba se vería bastante diferente.
class OrderInteractionTester... public void testOrderSendsMailIfUnfilled() { Order order = new Order(TALISKER, 51); Mock warehouse = mock(Warehouse.class); Mock mailer = mock(MailService.class); order.setMailer((MailService) mailer.proxy()); mailer.expects(once()).method("send"); warehouse.expects(once()).method("hasInventory") .withAnyArguments() .will(returnValue(false)); order.fill((Warehouse) warehouse.proxy()); } }
Para usar la verificación de estado en el talón, necesito hacer algunos métodos adicionales en el> talón para ayudar con la verificación. Como resultado, el apéndice implementa MailService pero agrega métodos adicionales de prueba.
En general, te gustaría burlarte de una interfaz.
Si bien es posible burlarse de una clase regular, tiende a influir demasiado en el diseño de su clase para probarla. Preocupaciones como la accesibilidad, si un método es virtual o no, etc. estarán determinadas por la capacidad de burlarse de la clase , en lugar de las verdaderas preocupaciones por OO.
Hay una biblioteca de simulación llamada TypeMock Isolator que le permite sortear estas limitaciones (tener pastel, comer torta) pero es bastante caro. Mejor diseñar para la capacidad de prueba.
En mi humilde opinión, lo que su colega quiere decir es que debe programar en una interfaz, no en una implementación . Si se burla de las clases con demasiada frecuencia, es una señal de que rompió el principio anterior al diseñar su arquitectura.
La burla se utiliza para las pruebas de protocolo : prueba cómo usará una API y cómo reaccionará cuando la API reaccione en consecuencia.
Idealmente (en muchos casos al menos), esa API debe especificarse como una interfaz en lugar de una clase: una interfaz define un protocolo, una clase define al menos parte de una implementación.
En una nota práctica, los marcos burlones tienden a tener limitaciones en torno a las clases de burla.
En mi experiencia, la burla es algo usada en exceso: a menudo no estás realmente interesado en la interacción exacta, realmente quieres un trozo ... pero se puede usar un marco burlón para crear talones, y caes en la trampa de crear pruebas frágiles burla en lugar de tropezar. Sin embargo, es un equilibrio difícil hacerlo bien.
La respuesta, al igual que la mayoría de las preguntas sobre prácticas, es "depende".
El uso excesivo de burlas puede llevar a pruebas que realmente no prueban nada. También puede conducir a pruebas que son implementaciones virtuales del código bajo prueba, estrechamente ligadas a una implementación específica.
Por otro lado, el uso prudente de los simulacros y trozos puede conducir a pruebas unitarias que están claramente aisladas y probar una cosa y una sola, lo cual es algo bueno.
Se trata de la moderación.
Las clases de burla (a diferencia de las interfaces burlonas) son malas porque el simulacro todavía tiene una clase real en el fondo, se hereda de, y es posible que la implementación real se ejecute durante la prueba .
Cuando se burla (o resguarda o lo que sea) de una interfaz, no existe el riesgo de que se ejecute el código que realmente quería simular.
Las clases de burla también te obligan a hacer que todo, que posiblemente se pueda burlar, sea virtual , lo cual es muy intrusivo y podría llevar a un mal diseño de clase .
Si quieres desacoplar clases, no deberían conocerse, esta es la razón por la cual tiene sentido simular (o resguardarse o lo que sea) uno de ellos. Por lo tanto, se recomienda implementar contra las interfaces de todos modos, pero esto es mencionado aquí por otros lo suficiente.
Sugeriría que se mantenga alejado de los marcos de burla en la medida de lo posible. Al mismo tiempo, recomendaría usar objetos falsos / falsos para probar, tanto como sea posible. El truco aquí es que debes crear objetos falsos integrados junto con objetos reales. Lo explico más detalladamente en una publicación de blog que escribí al respecto: http://www.yegor256.com/2014/09/23/built-in-fake-objects.html
Tiene sentido simular clases para que las pruebas se puedan escribir temprano en el ciclo de vida del desarrollo.
Existe una tendencia a seguir utilizando clases simuladas incluso cuando hay implementaciones concretas disponibles. También hay una tendencia a desarrollarse frente a las clases simuladas (y los apéndices) necesarios al principio de un proyecto cuando algunas partes del sistema no se han construido.
Una vez que se ha construido una parte del sistema, es necesario realizar pruebas en su contra y continuar la prueba en su contra (para la regresión). En este caso, comenzar con los simulacros es bueno, pero deben descartarse a favor de la implementación lo antes posible. He visto problemas en los proyectos porque diferentes equipos continúan desarrollándose contra el comportamiento del simulacro en lugar de la implementación (una vez que está disponible).
Al probar en contra de los simulacros, asumes que el simulacro es característico del sistema. A menudo esto implica adivinar lo que hará el componente burlado. Si tiene una especificación del sistema que se está burlando, entonces no tiene que adivinar, pero a menudo el sistema "como está construido" no coincide con la especificación original debido a consideraciones prácticas descubiertas durante la construcción. Los proyectos de desarrollo ágiles suponen que esto siempre sucederá.
Luego desarrolla código que funciona con el simulacro. Cuando resulta que el simulacro no representa realmente el comportamiento del sistema real tal como está construido (por ejemplo, problemas de latencia que no se ven en el simulacro, problemas de recursos y eficiencia que no se ven en el simulacro, problemas de concurrencia, problemas de rendimiento, etc.) tiene un montón de pruebas de burla inútiles que ahora debe mantener.
Considero que el uso de simulacros es valioso al inicio del desarrollo, pero estos simulacros no deberían contribuir a la cobertura del proyecto. Lo mejor es más adelante si se eliminan los simulacros y se crean pruebas de integración adecuadas para reemplazarlos; de lo contrario, su sistema no se someterá a pruebas para la variedad de condiciones que su simulacro no simuló (o simuló incorrectamente en relación con el sistema real).
Entonces, la pregunta es si usar o no los simulacros, es cuestión de cuándo usarlos y cuándo eliminarlos.