Estoy probando un método que es generar una colección de objetos de datos. Quiero verificar que las propiedades de los objetos se estén configurando correctamente. Algunas de las propiedades se establecerán en la misma cosa; otros se establecerán en un valor que depende de su posición en la colección. La forma natural de hacer esto parece ser con un bucle. Sin embargo, Roy Osherove recomienda enfáticamente no usar la lógica en las pruebas unitarias ( Art of Unit Testing , 178). Él dice:
Una prueba que contiene lógica generalmente está probando más de una cosa a la vez, lo que no se recomienda, porque la prueba es menos legible y más frágil. Pero la lógica de prueba también agrega complejidad que puede contener un error oculto.
Las pruebas deberían, como regla general, ser una serie de llamadas a métodos sin flujos de control, ni siquiera
try-catch
, y con llamadas de afirmación.
Sin embargo, no puedo ver nada malo en mi diseño (¿de qué otra manera genera una lista de objetos de datos, algunos de cuyos valores dependen de en qué lugar de la secuencia se encuentran? No se pueden generar y probar exactamente por separado). ¿Hay algo que no sea amigable con mi diseño? ¿O estoy siendo demasiado rígidamente dedicado a la enseñanza de Osherove? ¿O hay alguna magia secreta de prueba de unidad que no conozco que evita este problema? (Estoy escribiendo en C # / VS2010 / NUnit, pero si es posible estoy buscando respuestas independientes del lenguaje).
fuente
in
), si la prueba es "Frob se agregó con éxito a una colección existente".toString()
la Colección y la comparo con lo que debería ser. Simple y funciona.Respuestas:
TL; DR:
Lo primero para probar es que el dogma no es útil. Me gusta leer The Way of Testivus, que señala algunos problemas con el dogma de una manera alegre.
Si la prueba necesita ser escrita de alguna manera, escríbala de esa manera. Intentar forzar la prueba en un diseño de prueba idealizado o no tenerla no es algo bueno. Tener un examen hoy que lo pruebe es mejor que tener un examen "perfecto" algún día más tarde.
También señalaré el bit en la prueba fea:
Estos pueden considerarse truismos para aquellos que han estado siguiendo durante mucho tiempo ... y simplemente se arraigan en la forma de pensar y escribir pruebas. Para las personas que no han estado y están tratando de llegar a ese punto, los recordatorios pueden ser útiles (incluso encuentro que releerlos me ayuda a evitar quedar atrapado en algún dogma).
Tenga en cuenta que al escribir una prueba fea, si el código puede ser una indicación de que el código también está tratando de hacer demasiado. Si el código que está probando es demasiado complejo para ejercerlo correctamente escribiendo una prueba simple, es posible que desee considerar dividir el código en partes más pequeñas que se puedan probar con las pruebas más simples. No se debe escribir una prueba unitaria que haga todo (podría no ser una prueba unitaria entonces). Del mismo modo que los 'objetos de Dios' son malos, las 'pruebas de unidad de Dios' también son malas y deberían ser indicaciones para volver y mirar el código nuevamente.
Usted debe ser capaz de ejercer todo el código con una cobertura razonable a través de tales pruebas sencillas. Las pruebas que hacen más pruebas de extremo a extremo que se ocupan de preguntas más grandes ("Tengo este objeto, ordenado en xml, enviado al servicio web, a través de las reglas, de regreso y sin ordenar") es una prueba excelente, pero ciertamente no lo es Es una prueba unitaria (y cae en el ámbito de las pruebas de integración, incluso si se ha burlado de los servicios que llama y los ha personalizado en las bases de datos de memoria para hacer las pruebas). Todavía puede usar el marco XUnit para las pruebas, pero el marco de prueba no lo convierte en una prueba unitaria.
fuente
Estoy agregando una nueva respuesta porque mi perspectiva es diferente de cuando escribí la pregunta y la respuesta original; no tiene sentido juntarlos en uno solo.
Dije en la pregunta original
Aquí es donde me equivoqué. Después de hacer una programación funcional durante el último año, ahora me doy cuenta de que solo necesitaba una operación de recolección con un acumulador. Entonces podría escribir mi función como una función pura que operaba en una cosa y usar alguna función de biblioteca estándar para aplicarla a la colección.
Entonces, mi nueva respuesta es: use técnicas de programación funcional y evitará este problema la mayor parte del tiempo. Puede escribir sus funciones para operar en cosas individuales y solo aplicarlas a colecciones de cosas en el último momento. Pero si son puros, puede probarlos sin referencia a las colecciones.
Para una lógica más compleja, apóyate en pruebas basadas en propiedades . Cuando tienen lógica, debe ser menor e inversa a la lógica del código bajo prueba, y cada prueba verifica mucho más que una prueba unitaria basada en casos, por lo que vale la pena la pequeña cantidad de lógica.
Sobre todo, siempre apóyate en tus tipos . Obtenga los tipos más fuertes que pueda y úselos para su ventaja. Esto reducirá la cantidad de pruebas que tiene que escribir en primer lugar.
fuente
No intentes probar demasiadas cosas a la vez. Cada una de las propiedades de cada objeto de datos en la colección es demasiado para una prueba. En cambio, recomiendo:
Hacerlo de esta manera hace que las pruebas sean lo suficientemente pequeñas como para que dejar bucles no parezca doloroso. C # / Ejemplo de unidad, método dado bajo prueba
ICollection<Foo> generateFoos(uint numberOfFoos)
:Si está acostumbrado al paradigma de "prueba de unidad plana" (sin estructuras / lógica anidadas), estas pruebas parecen bastante claras. Por lo tanto, se evita la lógica en las pruebas al identificar el problema original como intentar probar demasiadas propiedades a la vez, en lugar de carecer de bucles.
fuente