¿Es una buena idea tener métodos de prueba separados para cada paso?

10

Estoy probando una API REST. Digamos que devuelve una estructura JSON. ¿Cuál es el mejor enfoque para probar el servidor? Cada paso de prueba solo puede tener éxito si todos los anteriores fueron exitosos.

Estructura A: prueba todo de una vez

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

Esta parece ser la mejor solución.

Ventajas:

  • Solo una solicitud de servidor
  • Estoy probando el comportamiento en su conjunto "¿El servidor devuelve un JSON con la clave X?"

Estructura B: agregue gradualmente afirmaciones a cada prueba

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

Así es como comencé a hacerlo y estaba convencido de que este debería ser el camino a seguir porque cada método prueba solo una cosa y esto crea una mejor separación. Pero ahora creo que, dado que no se trata de pruebas unitarias, mi separación no es apropiada y debo probar el comportamiento en su conjunto.

Estructura C: haga una solicitud una vez y ejecute métodos de prueba separados en la respuesta en caché

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

Ventajas:

  • Sin solicitud de servidor repetida (costosa)
  • Todavía tiene métodos de prueba de afirmación única

¿Cuál es la estructura de prueba más sensata para usar?

mrplow
fuente
¡Deje de cambiar su pregunta después de una manera que invalide las respuestas existentes! Gracias.
Doc Brown
Disculpe las molestias, pero ¿propondría hacerlo de otra manera?
mrplow
Primero, piense dos veces si realmente necesita cambiar su pregunta de esa manera. Si realmente cree que debe agregar algo que invalida algunas respuestas, puede informar a todos los autores de esas respuestas dejando un comentario debajo de su respuesta preguntándoles si quieren cambiar o agregar algo en su texto.
Doc Brown
2
De hecho, supuse que los autores de las respuestas SON notificados si se cambia la pregunta. Es por eso que no quería enviar comentarios no deseados con declaraciones fuera del tema. Notificaré a los autores en el futuro. Y gracias por dar una respuesta a mi pregunta.
mrplow

Respuestas:

3

Las mejores prácticas siempre tienen un propósito, una razón detrás de ellas. Siempre es una buena idea considerar estas razones en su diseño, especialmente cuando está tratando de decidir cómo y qué tan difícil es seguir estas mejores prácticas.

En este caso, el principal razonamiento detrás de hacer que cada prueba de prueba sea una sola cosa es que si la primera falla, la segunda no se probará. Dado que muchos creadores de opinión parecen encontrar mérito al dividir todo en los pedazos más pequeños posibles y envolver cada bit en la mayor hinchazón posible, esto dio a luz a la idea de que cada prueba debe contener una sola afirmación.

No sigas esto a ciegas. Incluso si cada prueba prueba una cosa, aún debe pensar en decidir qué tan grande o pequeña debe ser cada "cosa", y para hacerlo debe tener en cuenta por qué desea que cada prueba pruebe una cosa: para asegurarse un error en la primera cosa no deja la segunda cosa sin probar.

Entonces, debe preguntarse: "¿Realmente necesito esta garantía aquí?"

Digamos que hay un error en el primer caso de prueba: el código de respuesta HTTP no lo es 200. Entonces comienza a piratear el código, descubre por qué no obtuvo el código de respuesta que debería tener y soluciona el problema. ¿Y ahora qué?

  • Si vuelve a ejecutar manualmente la prueba, para verificar que su solución solucionó el problema, debe encontrarse con cualquier otro problema oculto por el primer error.
  • Si no lo ejecuta manualmente (¿tal vez porque toma demasiado tiempo?), Y simplemente empuja su solución esperando que el servidor de pruebas automatizadas ejecute todo, entonces es posible que desee colocar diferentes afirmaciones en diferentes pruebas. Los ciclos en este caso son muy largos, por lo que vale la pena hacer el esfuerzo de descubrir tantos errores en cada ciclo.

Hay algunas cosas más a tener en cuenta:

Afirmaciones de dependencias

Sé que las pruebas que describió son solo un ejemplo, y sus pruebas reales probablemente sean más complicadas, por lo que lo que voy a decir puede no ser válido con tanta fuerza en las pruebas reales, pero aún puede ser algo efectivo para que usted puede querer considerarlo.

Si tiene un servicio REST (o cualquier otro protocolo HTTP) que devuelve respuestas en formato JSON, generalmente escribe una clase de cliente simple que le permite usar los métodos REST como métodos regulares que devuelven objetos regulares. Suponiendo que el cliente tiene pruebas separadas para asegurarse de que funciona, ¡habría abandonado las primeras 3 afirmaciones y me quedaría solo 4!

¿Por qué?

  • La primera afirmación es redundante: la clase de cliente debería generar una excepción si el código de respuesta HTTP no es 200.
  • La segunda afirmación es redundante: si la respuesta está vacía, el objeto resultante será nulo o alguna otra representación de un objeto vacío, y no tendrá que colocar ninguna clave X.
  • La tercera afirmación es redundante: si el JSON no es válido, obtendrá una excepción cuando intente analizarlo.

Por lo tanto, no necesita ejecutar todas estas pruebas: solo ejecute la cuarta prueba, y si alguno de los errores que intentan detectar los primeros tres, la prueba fallará con una excepción adecuada antes de que obtenga la afirmación real.

¿Cómo quieres recibir los informes?

Supongamos que no recibe correos electrónicos de un servidor de prueba, sino que el departamento de control de calidad ejecuta las pruebas y le notifica las pruebas fallidas.

Jack de QA llama a tu puerta. Él dice que el primer método de prueba falló, y el método REST devolvió un código de respuesta erróneo. Le agradeces y comienzas a buscar la causa raíz.

Luego viene Jen de QA y dice que el tercer método de prueba falló: el método REST no devolvió un JSON válido en el cuerpo de la respuesta. Le dice que ya está viendo ese método, y cree que lo mismo que provocó que devolviera un código de salida incorrecto también provocó que devolviera algo que no es un JSON válido, y se parece más a un seguimiento de pila de excepción.

Vuelve a trabajar, pero luego llega Jim de QA, diciendo que el cuarto método de prueba falló y que no hay una tecla X en la respuesta ...

Ni siquiera puede buscar la razón porque es difícil ver el código cuando no tiene una pantalla de computadora. Si Jim fuera lo suficientemente rápido, podría haberlo esquivado a tiempo ...

Los correos electrónicos del servidor de prueba son más fáciles de descartar, pero aún así, ¿no preferiría que se le notifique UNA VEZ que algo está mal con el método de prueba y que usted mismo revise los registros de prueba relevantes?

Idan Arye
fuente
3

Si puede suponer con seguridad que realizar una solicitud de servidor con los mismos parámetros se comportará siempre igual, el método B es casi inútil: ¿por qué debería llamar cuatro veces al mismo método para obtener los mismos datos de respuesta cuatro veces cuando una llamada es suficiente?

Y si no puede asumir esto con seguridad y desea que forme parte de la prueba, es mejor que ejecute la prueba A varias veces.

La única situación hipotética que veo donde B podría tener un beneficio es cuando su marco de prueba permite que solo se activen y desactiven métodos de prueba explícitos, y usted espera la necesidad de hacerlo para los pasos individuales de su prueba.

La alternativa C parece combinar A con el beneficio que mencioné anteriormente para B. Si su marco de prueba permite que su código se estructura fácilmente de esa manera, sin mucha sobrecarga por encima de B, este es un enfoque factible. Sin embargo, esto agrega cierta complejidad adicional a A, por lo que solo lo usaría si alguna vez quisiera encender y apagar las pruebas individuales, de lo contrario, aplicar el principio YAGNI y apegarme a la solución más simple (A).

TLDR: comience con A si está seguro de que siempre desea que se ejecuten todas las afirmaciones en una prueba, refactorice a C si observa que necesita tener un control más fácil desde el exterior sobre las afirmaciones individuales.

Doc Brown
fuente
0

Como cualquier código, evite la optimización prematura. Primero escriba sus pruebas para que sean fáciles de leer y de mantener. Cuando las pruebas comienzan a ser demasiado lentas, optimícelas. En su ejemplo bastante simple, A y B serán fáciles de leer y mantener, así que elija el que desee hasta que las cosas se pongan demasiado lentas (estructura B) o demasiado complicadas (estructura A).

Si su servidor no tiene estado, entonces podría optimizar comparando la respuesta real con una respuesta esperada para verificar todo el mensaje de una vez. Obviamente eso será a expensas de la legibilidad.

Si su servidor está lleno de estado y necesita hacer múltiples llamadas API lentas para poner el servidor en un estado para la prueba, entonces debe adoptar un enfoque diferente o sus pruebas pueden tardar minutos en ejecutarse. Por ejemplo, podría ejecutar una actualización de la base de datos para inyectar datos en una base de datos de prueba para poder obtener rápidamente un objeto en un estado apropiado para la prueba. La prueba es rápida y legible pero más difícil de mantener. Alternativamente, puede escribir una fachada frente a la API para que varias llamadas de API se conviertan en una sola llamada de API que coincida más estrechamente con el proceso comercial que está probando.

Matt Helliwell
fuente
0

Las pruebas no deben compartir cosas: a partir de cero, se evita la influencia de una prueba en otra. Esto también le permite ejecutar pruebas en orden aleatorio.
Entonces el camino C no debe ser aceptado.


Al escribir cualquier código (o incluso crear cualquier otra cosa), siempre pregúntese: "¿por qué existe tal práctica?"
¿Por qué decimos que debería haber diferentes pruebas para todo?

Hay dos casos en los que necesita esto:

  1. cuando no puede confiar en "cada paso de prueba solo puede tener éxito si todos los anteriores fueron exitosos"
  2. cuando sus pruebas no tienen mensajes de afirmación descriptivos

Hay dos razones por las que enfrenta estos casos:

  1. "cada paso de prueba solo puede tener éxito si todos los anteriores fueron exitosos" realmente no es aplicable a la función de su producto
  2. no tiene suficiente conocimiento del producto debido a la falta de experiencia o tiempo, o la abrumadora complejidad del producto

Si por alguna razón no puede declarar que al menos uno de estos motivos tiene un lugar, simplemente tome la estructura B a ciegas .


De lo contrario (espero que llegue aquí) que elija una .


También puede hacer esta pregunta en el sitio Stackexchange de Software Quality Assurance & Testing .

Nakilon
fuente