Creación de pruebas unitarias en una capa CRUD de una aplicación, ¿cómo puedo hacer que las pruebas sean independientes?

14

Por lo tanto, estoy tratando de hacer que mis Pruebas de Unidad sean lo más exacta posible, pero se vuelve problemático cuando estoy probando algunos métodos simples de Agregar / Eliminar.

Para el método de agregar, básicamente tengo que crear un objeto ficticio y agregarlo, luego, después de que la prueba sea exitosa, tengo que eliminar el objeto ficticio.

Y para la prueba de eliminación, obviamente tengo que crear un objeto ficticio para poder eliminarlo.

Como puede ver si una prueba falla, la otra también fallará, ya que ambas son un poco necesarias.

Lo mismo con un sistema en el que necesitaría escribir una prueba que "cancela una orden" ... bueno, se necesitaría una orden ficticia para cancelar primero, ¿no va esto en contra de las pautas de las pruebas unitarias?

¿Cómo se manejan casos como este?

Kyle
fuente
También es posible que desee echar un vistazo a esta pregunta: programmers.stackexchange.com/questions/115455/…
Guven

Respuestas:

13

Bueno, no hay nada malo con lo que estás haciendo. Múltiples pruebas pueden cubrir el mismo código; solo significa que un problema hará que varias pruebas fallen. Lo que desea evitar son las pruebas que dependen de los resultados de otras pruebas. Es decir, su prueba de eliminación depende de que se haya ejecutado la prueba de adición y, por lo tanto, si ejecuta su prueba de eliminación ANTES de su prueba de adición, fallará. Para evitar ese problema, asegúrese de tener una "pizarra en blanco" al comienzo de cada prueba, de modo que lo que sucede en una prueba dada no pueda afectar las pruebas posteriores.

Una excelente manera de hacerlo es ejecutar las pruebas en una base de datos en memoria.

En su prueba de agregar, cree una base de datos vacía, agregue el objeto y afirme que efectivamente se ha agregado.

En su prueba de eliminación, cree la base de datos con el objeto que eliminará ya en ella. Elimine el objeto y afirme que se ha eliminado.

Elimine la base de datos en su código desmontable.

Adam Jaskiewicz
fuente
La base de datos en memoria es simplemente rápida (en memoria) y simple (en proceso). Podrías hacer esto con cualquier almacén de datos.
Paul Draper
3

Usar transacciones.

Si está utilizando una base de datos que admite transacciones, ejecute cada prueba en una transacción. Revierta la transacción al final de la prueba. Luego, cada prueba dejará la base de datos sin cambios.

Sí, para cancelar un pedido, primero tendrá que crear uno. Esta bien. La prueba creará primero un pedido, luego lo cancelará, luego verificará que el pedido se cancele.

Kevin Cline
fuente
Me encanta esta idea Implementado con gran efecto hoy.
pimbrouwers
3

Lo estás haciendo bien El único principio fundamental de las pruebas unitarias es cubrir cada ruta de código que tenga, de modo que pueda estar seguro de que su código está haciendo lo que se supone que debe hacer, y lo sigue haciendo después de los cambios y refactorizaciones. Mantener pruebas unitarias pequeñas, simples y de un solo propósito es un objetivo que vale la pena, pero no es fundamental. Tener una prueba que llame a dos métodos relacionados de su API no es en sí mismo cuestionable, de hecho, como usted señala, a menudo es necesario. El inconveniente de tener pruebas redundantes es que tardan más en escribir, pero como casi todo en desarrollo, esta es una compensación que debes hacer todo el tiempo, y la mejor solución casi nunca es uno de los puntos extremos.

Kilian Foth
fuente
2

Las técnicas de prueba de software son extremadamente variadas, y cuanto más se eduque acerca de ellas, comenzará a ver mucha orientación diferente (y a veces conflictiva). No hay un solo 'libro' para seguir.

Creo que estás en una situación en la que has visto alguna guía para las pruebas unitarias que dicen cosas como

  • Cada prueba debe ser independiente y no verse afectada por otras pruebas
  • Cada prueba unitaria debe probar una cosa, y solo una cosa
  • Las pruebas unitarias no deberían alcanzar la base de datos

y así. Y todo eso es correcto, dependiendo de cómo defina 'prueba unitaria' .

Definiría una 'prueba de unidad' como algo así como: "una prueba que ejercita una pieza de funcionalidad para una unidad de código, aislada de otros componentes dependientes".

Según esa definición, lo que está haciendo (si requiere agregar un registro a una base de datos antes de poder ejecutar la prueba) no es una 'prueba unitaria' en absoluto, sino más de lo que comúnmente se llama una 'prueba de integración'. (Una verdadera prueba unitaria, según mi definición, no afectará la base de datos, por lo que no necesitará agregar un registro antes de eliminarlo).

Una prueba de integración ejercerá la funcionalidad que utiliza múltiples componentes (como una interfaz de usuario y una base de datos), y la guía que se aplicaría a las pruebas unitarias no se aplica necesariamente a las pruebas de integración.

Como otros han mencionado en sus respuestas, lo que está haciendo no está necesariamente mal, incluso si hace cosas contrarias a alguna guía de prueba de unidad. En cambio, trate de razonar sobre lo que realmente está probando en cada método de prueba, y si encuentra que necesita múltiples componentes para satisfacer su prueba, y algunos componentes requieren una configuración previa, continúe y hágalo.

Pero, sobre todo, comprenda que hay muchos tipos de pruebas de software (pruebas unitarias, pruebas de sistema, pruebas de integración, pruebas exploratorias, etc.), y no intente aplicar la guía de un tipo a todos los demás.

Eric King
fuente
Entonces, ¿estás diciendo que no puedes eliminar la prueba unitaria de la base de datos?
ChrisF
Si está llegando a la base de datos, es (por definición) una prueba de integración, no una prueba unitaria. Entonces, en ese sentido, no. No puede 'eliminar la prueba de unidad' de una base de datos. Lo que puede hacer una prueba unitaria es que cuando se le pide al código que está probando que elimine algunos datos, interactúa correctamente con el módulo de acceso a datos.
Eric King
Pero el punto es que algunas personas pueden definir 'prueba unitaria' de manera diferente, por lo que debemos tener cuidado al aplicar la guía de 'prueba unitaria', ya que la guía puede no aplicarse de la manera que pensamos.
Eric King
1

Esta es exactamente la razón por la cual una de las otras pautas es usar interfaces. Si su método toma un objeto que implementa una interfaz en lugar de una implementación de clase específica, puede crear una clase que no dependa del resto de la base de código.

Otra alternativa es usar un marco burlón. Estos le permiten crear fácilmente este tipo de objetos ficticios que se pueden pasar al método que está probando. Es posible que deba crear algunas implementaciones de código auxiliar para la clase ficticia, pero aún así crea una separación de la implementación real y de lo que concierne a la prueba.

muestreador
fuente
1

Como puede ver si una prueba falla, la otra también fallará, ya que ambas son un poco necesarias.

¿Entonces?

... ¿no va esto en contra de las pautas de las pruebas unitarias?

No.

¿Cómo se manejan casos como este?

Varias pruebas pueden ser independientes y todas fallan debido al mismo error. Eso es realmente normal. Muchas pruebas pueden, indirectamente, probar alguna funcionalidad común. Y todos fallan cuando se rompe la funcionalidad común. No tiene nada de malo.

Las pruebas unitarias se definen como clases precisamente para que puedan compartir fácilmente el código, como un registro ficticio común utilizado para probar la actualización y la eliminación.

S.Lott
fuente
1

Puede usar un marco simulado o usar un 'entorno' con una base de datos en memoria. La última es una clase donde puede crear todo lo que necesita para pasar la prueba, antes de que se ejecute la prueba.

Prefiero el último: los usuarios pueden ayudarlo a ingresar algunos datos para que sus pruebas se acerquen más al mundo real.

Andre
fuente
Es cierto, pero en realidad no está probando la conexión real de la base de datos aquí. A menos que asumas que eso siempre va a funcionar, pero las suposiciones son peligrosas.
ChrisF