Unidad probando un cliente API y envoltorios

14

He estado dando vueltas en círculos tratando de encontrar la mejor manera de probar en unidad la biblioteca cliente API que estoy desarrollando. La biblioteca tiene una clase Client que básicamente tiene una asignación 1: 1 con la API, y una clase adicional Wrapper que proporciona una interfaz más fácil de usar sobre la parte superior del Client .

Wrapper --> Client --> External API

Primero escribí un montón de pruebas contra Client y Wrapper , efectivamente solo probando que reenvían a las funciones apropiadas de cualquier operación ( Wrapper opera en Client y Client opera en una conexión HTTP). Sin embargo, empecé a sentirme incómodo con esto porque siento que estoy probando la implementación de estas clases, en lugar de la interfaz. En teoría, podría cambiar las clases para tener otra implementación perfectamente válida, pero mis pruebas fallarían porque las funciones a las que esperaba que me llamen no están siendo llamadas. Eso me suena a pruebas frágiles.

Después de esto, pensé en la interfaz de las clases. Las pruebas deben verificar que las clases realmente hacen el trabajo que deben hacer, en lugar de cómo lo hacen. Entonces, ¿cómo puedo hacer esto? Lo primero que viene a la mente es aplastar las solicitudes API externas. Sin embargo, estoy nervioso por simplificar demasiado el servicio externo. Muchos de los ejemplos de API aplastadas que he visto solo dan respuestas enlatadas, lo que suena como una forma realmente sencilla de probar que su código se ejecuta correctamente contra su API falsa. La alternativa es burlarse del servicio, que es simplemente inviable, y debería mantenerse actualizado cada vez que cambie el servicio real, lo que se siente como una exageración y una pérdida de tiempo.

Finalmente, leí esto en otra respuesta en los programadores SE :

  

El trabajo de un cliente API remoto es emitir ciertas llamadas, ni más, ni menos. Por lo tanto, su prueba debe verificar que emita esas llamadas, ni más ni menos.

Y ahora estoy más o menos convencido: al probar Client , todo lo que necesito probar es que realiza las solicitudes correctas a la API (por supuesto, siempre existe la posibilidad de que la API cambie, pero mis pruebas continuar aprobando, pero ahí es donde las pruebas de integración serían útiles). Dado que Client es solo un mapeo 1: 1 con la API, mi preocupación antes de cambiar de una implementación válida a otra no se aplica en realidad, solo hay una implementación válida para cada método de Client .

Sin embargo, todavía estoy atascado con la clase Wrapper . Veo las siguientes opciones:

  1. Apago la clase Client y solo pruebo que se llaman los métodos apropiados. De esta manera, estoy haciendo lo mismo que arriba, pero tratando el Client como un complemento para la API. Esto me pone de vuelta donde comencé. Una vez más, esto me da la incómoda sensación de probar la implementación, no la interfaz. El Wrapper podría implementarse utilizando un cliente completamente diferente.

  2. Creo un Client simulado. Ahora tengo que decidir qué tan lejos ir con la burla, crear un simulacro completo del servicio llevaría mucho esfuerzo (más trabajo del que se ha invertido en la biblioteca) La API en sí es simple, pero el servicio es bastante complejo (es esencialmente un almacén de datos con operaciones en esos datos). Y nuevamente, tendré que mantener mi simulacro sincronizado con el verdadero Client .

  3. Acabo de probar que se están realizando las solicitudes HTTP adecuadas. Esto significa que Wrapper llamará a través de un objeto real Client para realizar esas solicitudes HTTP, por lo que no lo estoy probando de forma aislada. Esto lo convierte en una prueba un tanto terrible.

Así que no estoy particularmente contento con ninguna de estas soluciones. ¿Qué harías? ¿Hay una forma correcta de hacer esto?

    
pregunta Joseph Mansfield 23.12.2015 - 01:50

1 respuesta

10

TLDR : a pesar de la dificultad, debe detener el servicio y usarlo para las pruebas de la unidad del cliente.

No estoy tan seguro de que el "trabajo de un cliente API remoto sea emitir ciertas llamadas, ni más, ni menos ...", a menos que la API solo consista en puntos finales que siempre devuelven un estado fijo, y ninguno Consumir ni producir ningún dato. Esta no sería la API más útil ...

También desearía verificar que el cliente no solo envíe las solicitudes correctas, sino que también maneje adecuadamente el contenido de las respuestas, los errores, las redirecciones, etc. Y realice pruebas en todos estos casos.

Como debe observar, debe tener pruebas de integración que cubran la pila completa desde el envoltorio - > cliente - > servicio - > DB y más allá, pero para responder a su pregunta principal, a menos que tenga un entorno donde se puedan ejecutar pruebas de integración como parte de cada compilación de CI sin muchos dolores de cabeza (bases de datos de pruebas compartidas, etc.), debería invertir tiempo en crear una talón de la API.

El código auxiliar le permitirá crear una implementación de trabajo del servicio, pero sin tener que implementar ninguna capa debajo del servicio en sí.

Podría considerar el uso de una solución basada en DI para lograr esto, con una implementación del patrón de Repositorio debajo de los recursos REST:

  • Reemplace todos los códigos funcionales en los controladores REST con llamadas a un IWhateverRepository.
  • Cree un ProductionWhateverRepository con el código que se extrajo de los recursos REST y un TestWhateverRespository que devuelve respuestas enlatadas para su uso durante las pruebas unitarias.
  • Use el contenedor DI para inyectar la clase / DLL ProductionWhateverRepository o TestWhateverRepository, etc., según la configuración.

De todos modos, a menos que sea implacable, ya sea políticamente o prácticamente, el rechazo y / o la refactorización del servicio, me gustaría emprender algo similar a lo anterior. Si no fuera posible, me aseguraría de tener una cobertura de integración realmente buena y las ejecutaría tan a menudo como sea posible dada la configuración de prueba disponible.

HTH

    
respondido por el Dan1701 23.12.2015 - 06:40

Lea otras preguntas en las etiquetas