Como practicante de TDD "simulado", ¿debería burlarme de otros métodos en la misma clase que el método bajo prueba?
mocking (7)
Después de leer Mocks Are not Stubs , de Martin Fowler, descubrí que he estado practicando TDD de forma "burlona".
Pero me pregunto si incluso en la TDD burlona si uno puede tomar burlas demasiado lejos.
Aquí hay un ejemplo actualizado en pseudo-código de estilo Python:
def sync_path(self):
if self.confirm_or_create_connection():
self.sync(self.dirpath)
El método confirm_or_create_connection () crea una conexión a un servidor.
Probé un método similar a este en dos pruebas, las cuales simulamos confirm_or_create_connection () y sync () (aunque ambos son métodos en la misma clase). En una prueba, el simulador confirm_or_create_connection () devuelve True y la prueba confirma que se llamó a sync (), y en el otro el simulacro confirm_or_create_connection () devuelve False y la prueba confirma que no se llamó a sync ().
Es esto razonable? ¿O debería burlarme de los objetos que confirman la llamada de confirm_or_create_connection () y sync ()? (Tengo otras pruebas de estos dos métodos que ya hacen esto).
Por favor, no respondas la pregunta explicando que debería estar practicando el TDD "clásico" en su lugar. Esa es una respuesta a otra pregunta: ¿Debo practicar TDD clásico o clásico?
Personalmente creo que burlarse de uno mismo es casi siempre un olor a código. Está probando la implementación en lugar del comportamiento.
editado para el nuevo ejemplo
Para mí, parece que estás borrando confirm_or_create_connection, solo estás interesado en definir la devolución de llamada y estás simulando la sincronización, aquí estás interesado en probar si realmente se llama. (Tendré que verificar si mi definición de trozo o burla es la misma que la del artículo de Fowler que mencionaste. Ha pasado algún tiempo desde que la leí y he estado usando rhinomocks en c # que podría tener su propia defensa de estos términos :-) )
Creo que para lo que está probando burlarse y pegar esas llamadas es la manera correcta de hacerlo. No desea que la prueba falle si una de esas funciones tiene un error, existen otras pruebas para eso. Solo quiere probar la operación de sync_path.
Estoy de acuerdo con Avdi en que esto es algo maloliente. Las pruebas están bien, pero tu clase podría estar haciendo demasiado.
¿Puedes burlarte demasiado? No sé demasiado, pero se puede hacer mal, de modo que en realidad estás probando los simulacros en lugar del código, o peor, para que tengas pruebas frágiles.
Pero siempre y cuando esté escribiendo buenas pruebas, pruebas que confirmen su comportamiento esperado, pruebas que lo ayudan a escribir el código, ¡entonces fingir!
La técnica se llama "objetos simulados", no "métodos falsos" por una razón. Fomenta diseños que dividen el sistema en objetos de fácil composición y colaboración, y lejos del código de procedimiento. El objetivo es elevar el nivel de abstracción para que pueda programar al componer objetos y rara vez escribir declaraciones de flujo de control de bajo nivel.
Editado para muestra actualizada:
Ya lo veo. Tienes problemas para probar esta clase porque tiene fallas de diseño. Esta clase viola el principio de responsabilidad individual. Está haciendo dos cosas. Primero, es administrar una conexión a una base de datos. También se está sincronizando.
Necesita una clase separada para administrar su conexión a la base de datos. Esta clase será una dependencia de la clase bajo prueba. La clase de conexión de la base de datos se puede simular cuando se prueba la clase bajo prueba.
Antes:
Como compañero tester de interacción, considere refactorizar si necesita hacerlo. Esa clase probablemente está haciendo demasiado.
Déjame ponértelo de esta manera: llamar a un método privado no hace una interacción.
Este es uno de los principales puntos de TDD. Cuando duele, tu diseño puede mejorarse.
Adivinando salvajemente, parece que la actividad de conexión podría pertenecer a otro objeto al que debería delegarse, en cuyo caso puede burlarse de eso . Por lo general, recomiendo evitar burlarse de una parte de un objeto para probar otra parte. Sugiere que hay dos conceptos unidos entre sí.
Aquí hay una buena lectura: "Principio: No modifique el SUT" en http://xunitpatterns.com/Principles%20of%20Test%20Automation.html#Don
Modificar la clase que está probando mediante burlas o trozos de su implementación es un olor a código. La refactorización para alejarse de ella es mover la parte que estás burlando / tropezando a otra clase. Eso dijo que no siempre es algo terrible. Es un olor a código, pero no siempre es inapropiado. Para lenguajes como C # o Java donde tiene buenas herramientas de refactorización, es fácil corregir este código y normalmente lo haría (en C #, suponiendo que Java sea similar). Hago mucho desarrollo en Lua y Javascript, donde las cosas son un poco diferentes. Crear y administrar muchas clases en esos idiomas es más difícil, por lo que soy más tolerante con la modificación del SUT en las pruebas. Siempre es algo que puedo arreglar más tarde una vez que la cobertura de la prueba inicial está allí. Requiere cuidado extra.