2012-05-13 23 views
17

En mi último proyecto, teníamos pruebas unitarias con casi 100% de cc, y como resultado, casi no tuvimos ningún error. Sin embargo, dado que Unit Testing debe ser White Box (debe burlarse de las funciones internas para obtener el resultado que desea, entonces sus pruebas deben conocer la estructura interna de su código) cada vez que cambiamos la implementación de una función, teníamos para cambiar las pruebas también. Tenga en cuenta que no cambiamos la lógica de las funciones, solo la implementación. consumió mucho tiempo y parecía que estábamos trabajando de forma incorrecta. Como usamos todas las pautas de OOP adecuadas (específicamente Encapsulation), cada vez que cambiamos la implementación no tuvimos que cambiar el resto de nuestro código, sino que tuvimos que cambiar las pruebas de la unidad. Parecía que estamos sirviendo las pruebas, en lugar de que nos sirvan.Black Box Unit Testing

Para evitar esto, algunos de nosotros argumentamos que las pruebas unitarias deberían ser Black Box Testing. Eso sería posible si creamos un gran simulacro de todo nuestro Dominio y creamos un talón para cada función en cada clase en un solo lugar, y lo usamos en cada prueba unitaria. Por supuesto que si una prueba específica necesita una función interna específica para ser llamada (como para asegurarnos de escribir en la base de datos), podemos anular nuestro código auxiliar.

Por lo tanto, cada vez que cambiemos la implementación de una función (como agregar o reemplazar una llamada a una función de ayuda) solo tendremos que cambiar nuestra gran simulación principal. Incluso si necesitamos cambiar algunas pruebas unitarias, todavía será mucho menos que antes.

Otros argumentan que las pruebas unitarias deben ser White Box, ya que no solo quieres asegurarte de que tu aplicación escribe en el DB en un lugar específico, debes asegurarte de que tu aplicación no escriba en el DB en ningún otro lado a menos que específicamente espero que lo haga. Si bien este es un punto válido, no creo que valga la pena el tiempo de escribir pruebas de White Box en lugar de las pruebas de Black Box.

Así que en conclusión, 2 preguntas:

  1. ¿Qué opinas sobre el concepto de caja Negro Unidad de Pruebas?

  2. ¿Qué opinas sobre la forma en que queremos implementar ese concepto? ¿Tienes mejores ideas?

Respuesta

6

y como resultado que casi no tiene virus

Si fuera realmente capaz de lograr esto, entonces no creo que debería cambiar nada.

Las pruebas de caja negra pueden sonar atractivas en el papel, pero la verdad es que casi siempre necesita conocer partes del funcionamiento interno de una clase probada. El proporciona entrada, verificar salida en realidad funciona solo para casos simples. La mayoría de las veces sus pruebas deben tener al menos algún conocimiento del método probado: cómo interactúa con colaboradores externos, qué métodos llama, en qué orden, etc.

Toda la idea detrás del diseño burdo y SOLIDO es evitar la situación en la que implementación de dependencia cambiar causa otros cambios/fallas en la prueba de clase. Por el contrario, si cambia los detalles de implementación del método probado, entonces debe cambiar los detalles de implementación de las pruebas. Eso no es nada fuera de lo común.

En general, si realmente pudieras lograr casi sin errores, entonces me quedaría con ese enfoque.

+1

"si cambia los detalles de implementación del método probado, también debe cambiar los detalles de implementación de las pruebas" No lo pensé de esa manera, gracias. – user1392027

+0

"si cambia los detalles de implementación del método probado, entonces debería cambiar los detalles de implementación de las pruebas" - ¿eh? ¿Has oído hablar de TDD, donde dicen que no deberías escribir ningún código hasta que haya una prueba fallida? – driushkin

+2

@driushkin: estamos hablando de una situación donde la prueba y el código ya están escritos. Para citar OP, * "tenga en cuenta que no cambiamos la lógica de las funciones, solo la implementación" * - este tipo de cambio ** ** dará como resultado la necesidad de cambiar las pruebas, lo más probable. –

2

Creo que deberías continuar escribiendo pruebas unitarias, solo hazlas menos frágiles.

Las pruebas unitarias deben ser de bajo nivel pero deben probar el resultado y no cómo se hacen. Cuando el cambio de implementación causa un gran cambio de prueba, significa que en lugar de probar los requisitos, en realidad está probando la implementación.

Existen varias reglas básicas, como "no probar métodos privados" y el uso de objetos simulados.

Burlarse/simular todo el dominio suele dar como resultado lo opuesto a lo que intentas lograr: cuando el código cambia el comportamiento necesitas actualizar las pruebas para asegurarte de que tus "objetos simulados" se comportan igual. realmente muy rápido a medida que aumenta la complejidad del proyecto.

Sugiero que continúe escribiendo pruebas unitarias, solo aprenda cómo hacerlas más robustas y menos frágiles.

+0

El problema es que en muchos casos nuestro método público llama a 2-3 métodos privados que lo ayudan. Al probar los métodos públicos, debemos burlarnos de los privados. En cualquier caso, no probamos nuestros métodos privados. – user1392027

+2

@ user1392027 ¿por qué simular los métodos de ayuda privada? – mlvljr

+1

Acepto, ¿por qué simular los métodos de ayuda privada? Eso es una tontería. Las pruebas de su unidad deben probar solo la * funcionalidad * de la unidad, que se expresa mediante su interfaz. En el mundo de OO, la interfaz de la unidad es el método público de un objeto.Si las pruebas de su unidad dependen de los métodos privados, entonces son frágiles y es natural que termine con la situación horrible cuando cambia las pruebas después de cambiar solo una implementación de una función. – hijarian

11

Necesita diferentes tipos de pruebas.

  • unidad de pruebas que deben ser pruebas de caja blanca, como lo hizo

  • Pruebas de integración (o pruebas del sistema) que ponen a prueba la capacidad de utilizar las implementaciones reales de su sistema y su comunicación con el exterior capas (sistemas externos, base de datos, etc.) que deben tener un estilo de caja negra, pero cada una para una característica específica (pruebas CRUD por ejemplo)

  • Pruebas de aceptación que deben ser completamente negras y son controladas por funcional requisitos (ya que sus usuarios los expresarían). End-to-end tanto como sea posible, y sin conocer el interno de las implementaciones elegidas. La definición de libro de texto de pruebas de caja negra.

Y recuerde que la cobertura del código no tiene sentido en la mayoría de los casos. Necesita una cobertura de líneas elevadas (o cobertura de métodos, cualquiera que sea su método de conteo), pero eso generalmente no es suficiente. El concepto que debe tener en cuenta es Cobertura funcional: asegúrese de que se cubran todos sus requisitos y rutas lógicas.

+0

No realizamos check-in si hay pruebas unitarias que fallan. Las pruebas de integración fallarán duramente durante el desarrollo, por lo que no nos serán útiles. Tenemos pruebas de integración manual que ejecutamos durante la integración. – user1392027

+0

Si tiene pruebas de integración que fallan temporalmente, podría tenerlas en una compilación diferente, y solo considerar su "compilación principal" para checkin – Guillaume

+0

El objetivo no es la compilación en sí (aunque sea importante). No hacemos check-in si hay pruebas unitarias que fallan, ya que no queremos "propagar" nuestros propios errores a otros desarrolladores. Lo que necesitamos es una manera fácil de decir si tenemos errores o no en algún código que acabamos de escribir. Las pruebas de integración, suponiendo que las ejecute en integración, no nos ayudarán en este caso. – user1392027

1

"como resultado casi no tuvimos ningún error", así que mantenlo así. La única causa de frustración es la necesidad de mantener las pruebas unitarias, que en realidad no es tan mala (la alternativa es mucho peor). Solo hazlos más sostenibles. "El arte de Unit Testing" de Roy Osherove me dio un buen comienzo de esta manera. Entonces 1) No es una opción. (La idea en sí contradice los principios de TDD, por ejemplo) 2) Usted tendrá muchos más problemas de mantenimiento con dicho enfoque. La filosofía de la prueba unitaria es desconectar el SUT de otro sistema y probarlo usando stubs como entrada y burlarse como salida (¿señales?) Simulando situaciones de la vida real (o mb. Simplemente no capto la idea de "un gran simulacro de todo nuestro Dominio").

6

tl; dr versión:

  1. prueba de la unidad Box Negro es exactamente como la unidad de pruebas debe hacerse.
  2. Las pruebas de unidad de caja negra son exactamente cómo se deben realizar las pruebas unitarias. La práctica adecuada de TDD hace exactamente esto.

Versión completa.

No hay absolutamente ninguna necesidad de probar métodos privados de los objetos. Tampoco tendrá ningún impacto en la cobertura del código.

Cuando usa una clase TDD, escribe pruebas que verifican el comportamiento de esa clase. El comportamiento se expresa a través de los métodos públicos de esa clase. Nunca debería molestarse con la forma en que los métodos realmente se implementan. la gente de Google describen que mucho mejor de lo que nunca será capaz de: http://googletesting.blogspot.ru/2013/08/testing-on-toilet-test-behavior-not.html

Si lo hace el error habitual y estáticamente depender de otras clases de entidad o, peor aún, en las clases de las diferentes capas de aplicación, es inevitable que se quiere encontrarse en una situación en la que necesite comprobar muchas cosas en su prueba y preparar muchas cosas para ello. Para resolver esto, existe el principio de Inyección de Dependencia y el Law of Demeter.