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?
testing
acceptance-testing
mrplow
fuente
fuente
Respuestas:
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é?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é?
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?
fuente
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.
fuente
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.
fuente
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:
Hay dos razones por las que enfrenta estos casos:
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 .
fuente