Probar un cliente REST contra un servidor REST. ¿Cómo hacer accesorios?

10

Al escribir pruebas unitarias, es común usar accesorios: pocos datos comprobables, por lo que podemos decir: 1. Obtener todos los clientes deben incluir a Willy Wonka. 2. Elimine el cliente 3, y ahora obtener clientes ya no debería incluir a Willy Wonka.

Eso está bien para pruebas unitarias. Utilice la configuración / desmontaje para volver a cargar los dispositivos o deshacer la transacción. Por lo tanto, las pruebas de creación, actualización y eliminación se realizan dentro de una transacción . Los nuevos datos temporales duran solo la duración de esa prueba, luego se restablecen.

¿Pero qué pasa cuando separamos el servidor REST del cliente REST?

Queremos asegurarnos de que nuestro cliente REST no solo lea correctamente, sino que cree, actualice y elimine correctamente.

No he podido encontrar ningún ejemplo o sugerencia sobre cómo hacer esto en un servidor REST de prueba remoto.

Suponiendo que tengo un servidor REST de prueba que solo sirve accesorios. Toda la naturaleza sin estado de HTTP significa que sería difícil enviar un mensaje de tipo "COMENZAR TRANSACCIÓN" y "TRANSACCIÓN ROLLBACK" o "RELOAD FIXTURES", ¿verdad?

No puedo ser el primero en querer hacer esto, así que tengo la sensación de que necesito una forma diferente de pensar sobre esto.

¿Alguna sugerencia?

Sivers
fuente
Tal vez, dado que es un servidor de prueba, ¿puede tener un punto final que vuelva a cargar los dispositivos?
David Radcliffe
Si su principal problema es devolver el servidor de prueba a un estado predefinido, ¿por qué no agrega algún tipo de funciones de prueba especiales como "RELOAD TESTDATA" a su API de descanso para hacer lo que desea? Por supuesto, debe asegurarse de que ese tipo de llamadas API no esté disponible en producción.
Doc Brown

Respuestas:

7

Los sistemas de software idealmente tienen límites de sistema bien definidos e interfaces entre ellos. Los servicios REST son buenos ejemplos de esto.

Con ese fin, recomendaría no hacer lo que intentas hacer.

Específicamente:

Queremos asegurarnos de que nuestro cliente REST no solo lea correctamente, sino que cree, actualice y elimine correctamente.

Lo que sugeriría en su lugar es:

  • Creación de pruebas para su cliente REST, para asegurarse de que se comporta correctamente, dada la entrada y salida específicas. Tenga en cuenta los valores buenos (esperados) y malos (inesperados).

  • Creación de pruebas para su servicio REST (si lo controla, es decir), para comportarse de acuerdo con su función prevista

  • Mantenga las pruebas cerca de su dominio problemático, para que puedan ayudar a guiar el diseño y desarrollo de lo que es importante en ese contexto.

briandoll
fuente
3
Usted descarta toda la idea de las pruebas de integración aquí de manera bastante casual. No creo que este enfoque se base en la práctica.
Febeling
Gracias a todas las sugerencias útiles. También por Twitter recibí excelentes sugerencias para probar Ruby gem "webmock" y similar para burlarse de la respuesta del servidor REST API. También estoy de acuerdo con "febeling" en que lo que estoy describiendo parece ser más una prueba de integración, así que lo analizaré por separado. Gracias de nuevo a todos. - Derek
Sivers
burlarse de una API es una excelente manera de resolver el problema. Pero, ¿cómo se asegura de que la API burlada == API real?
FrEaKmAn
4

Dos ángulos a tener en cuenta aquí:

  • ¿Estás probando tu código o la tubería? Suponiendo que está utilizando un servicio bien conocido y una pila de clientes, probablemente pueda presumir de forma segura sus probadores y miles de usuarios generalmente se asegurarán de que no haya un error fundamental en las bases.
  • ¿Por qué sus pruebas no son idempotentes? Haga una forma de escribir datos que no sean de producción o escriba en un punto final diferente. Elija un patrón de nombres predecible. Precargue la base de datos del servidor de descanso antes de las pruebas. Y probablemente haya algunas formas más de hacer que esto suceda: el método es realmente táctico y debe depender de la naturaleza de la aplicación.
Wyatt Barnett
fuente
1

Como han dicho otros, si está probando un cliente, no necesita ir tan lejos como crear, eliminar, etc. en el servidor. Muchas veces ni siquiera necesitas burlarte de un servidor. Realmente solo necesita asegurarse de que está haciendo las solicitudes correctas y manejando correctamente las respuestas, ya sea que esté escrito en Ruby, Python, PHP o cualquier otra cosa, en algún momento su cliente probablemente usará un método de una biblioteca HTTP para hacer una solicitud y es suficiente para burlarse de ese método, verificar cómo se llama y devolver el resultado de la prueba.

Tome un cliente hipotético de Python que utiliza urllib2para realizar solicitudes. Probablemente tenga algún método en el cliente, llamémoslo get(), que tenga una llamada urllib2.Request(). Realmente solo necesita burlarse de la llamada a su propia clase get().

@patch('your.Client.get')
def test_with_mock(self, your_mock):
    your_mock.return_value({'some': 'json'})
    test_obj = your.Client.get_object(5)
    your_mock.assert_called_with('/the/correct/endpoint/5')

Este ejemplo muy simplificado usa la biblioteca Mock de Python para probar una your.Clientclase hipotética con un get_object()método que genera la url correcta para obtener algo de alguna API. Para realizar la solicitud, el cliente llama a su get()método con esa url. Aquí, ese método se burla ( your.Client.getse "parchea" para que esté bajo el control de your_mock), y la prueba verifica si se solicitó el punto final correcto.

El método simulado devuelve la respuesta JSON configurada ( your_mock.return_value) que el cliente debe manejar y usted haría más afirmaciones para probar que manejó los datos esperados de la manera esperada.

Sean Redmond
fuente
Pero, ¿cómo está seguro de que cuando realiza la solicitud "correcta" es una solicitud real correcta (en producción)? Porque si entiendo tu sugerencia, si la API cambia o se rompe, tus pruebas seguirán funcionando. Mientras que en producción es una historia totalmente diferente.
FrEaKmAn
1

Lo que describe es un escenario de prueba de integración. Estos suelen ser un poco incómodos de configurar y derribar. Hace que corran lentamente y con frecuencia quebradizos.

El enfoque con los accesorios es tan incómodo y torpe, pero es la forma predeterminada en que algunos marcos lo hacen, por ejemplo, Rails, y ya es compatible. Necesitan el caso de prueba abstracta o algo similar para preparar la base de datos con accesorios. (Tenga cuidado con los nombres inusuales de las categorías de prueba en Rails, las pruebas unitarias con accesorios DB son estrictamente hablando también pruebas de integración).

La forma en que abordaría su escenario es aceptar tener un control específico de prueba sobre el estado de la aplicación API o su base de datos. Puede tener puntos finales adicionales para la configuración y el desmontaje, que solo están presentes en el entorno de prueba. O, alternativamente, usted habla con la base de datos (o lo que sea que esté usando) detrás de su aplicación / API.

Si cree que esto es demasiado (en el sentido de un esfuerzo indebido), considere que el enfoque con accesorios para bases de datos hace exactamente eso: usar medios adicionales específicos de la prueba para manipular la base de datos o el estado de la aplicación.

Sin embargo, no creo que esta discusión tenga que ver con la naturaleza sin estado de HTTP. HTTP no tiene estado, pero la aplicación definitivamente no lo es, en la mayoría de los casos. Suena un poco como si buscaras la rigidez REST. También podría hacer que todos los recursos sean totalmente creables, legibles y eliminables. En ese caso, podría hacer toda la configuración y desmontaje a través de los medios API normales. Sin embargo, esto a menudo no se hace en la práctica, porque no desea incluir ciertas operaciones desde una comprensión comercial de su aplicación, al menos no fuera del entorno de prueba.

febeling
fuente
1

Parche mono

En mi trabajo, hacemos ATDD mediante el uso de un marco xUnit existente y llamadas de red de parches de mono entre el cliente y el servidor. En el mismo espacio de proceso que cargamos al cliente, monkey parchea la llamada de red en la parte superior del código de pila del servidor REST. Todas las llamadas se emiten desde el cliente como lo harían normalmente, y el código del servidor recibe las solicitudes exactamente como aparecerían normalmente.

Beneficios

  • sin tener que sincronizar con el inicio del servidor (porque no hay servidor)
  • Aproveche la configuración clásica de la unidad y el método de desmontaje para administrar cosas como accesorios
  • capacidad de usar simulacros / trozos y otros parches de mono para un control más fino de la prueba
  • se puede escribir usando un marco xUnit

Compensaciones

  • no expone las interacciones / problemas de múltiples procesos (bloqueo, falta de recursos, etc.)
  • no expone problemas de servidores múltiples (serialización de datos, estilo de agrupación)
  • no expone problemas de red porque eso es simulado (acceso, errores de tiempo de espera, etc.)
dietbuddha
fuente