Testear servicios que se conectan a una API con Webmock, Rspec y Ruby on Rails

Si hay algo que debemos evitar al momento de realizar tests en nuestra aplicación, son las consultas a servicios externos. Uno se podría preguntar porqué, y la respuesta no es solo una, ya que realizar esto podría implicar los siguientes inconvenientes:

  • Tener inconsistencia intermitente al correr los tests debido a problemas de conectividad
  • Aumentar drásticamente el tiempo de los tests
  • Si la API a la que consultamos tiene un límite de consultas, corremos el riesgo de agotarlo
  • Podemos llegar a estar creando, editando o eliminando información real

Por suerte, existen herramientas que pueden ahorrarnos todos estos inconvenientes. La gema que utilizaremos para hacerlo es Webmock, y para agregarla sólo debemos incluir en nuestro gemfile:

y hacer un require en el archivo ‘spec/spec_helper.rb’:

y si queremos evitar cualquier comunicación http real, podemos agregar en el mismo archivo:

Con esta configuración, Rspec va a denegar cualquier solicitud http al exterior. Supongamos que nuestra conexión a la API está hecha a través del siguiente servicio, que se encarga de buscar cual es el último campeón del mundo, aka, Argentina:

Como podemos ver, en el servicio tenemos 2 tipos de respuestas, una cuando la API nos retorna el último campeón y otra cuando hay algún inconveniente y no es capaz de hacerlo. Con esto en mente, podríamos armar un test como el siguiente:

Debido a la configuración que hicimos previamente, al intentar correr el test se hace un request a la siguiente url: ‘http://example.com/api/current_fifa_world_cup_champion’.

Como tenemos prohibidas las conexiones al exterior, tendremos un error como el siguiente:

Para solucionar este inconveniente, lo que debemos hacer es un stub del request que va a realizar la API, para que cuando esto suceda, lo intercepte antes de que intente ir a buscar información al exterior. En nuestro test, los stubs quedarían así:

Con esto hecho, ya no se sale a buscar la url verdadera, y la respuesta que se obtiene de la API, es aquella que nosotros le hayamos configurado en el stub. Además, como tenemos stubeadas las respuestas, en vez de esperar que la respuesta no sea diferente de ‘nil’, podemos definir que respuesta exacta estamos esperando.

Como este es un ejemplo simple, se pudieron realizar los stubs dentro del mismo test, sin que éste pierda legibilidad. Lo que solemos hacer en casos que esto no sea posible, es crear un servicio que se encargue de mockear dichos stubs, y que éste a su vez lea las respuestas de un directorio, así podemos tener todo más ordenado y con el código más limpio.

Vamos a crear nuestro primer mock ‘example_mock.rb’ para nuestro test, en el directorio ‘../spec/mocks’’:

Como podemos ver en este mock, estamos definiendo los mismos stubs que antes teníamos directamente en el test, por lo que ahora podríamos invocar a los métodos que se encargan de esto, y así alivianar el código dentro del test. Al definir el método ‘json_response’, nos permitimos limpiar el código de nuestros mocks, organizando las respuestas dentro de un directorio en la aplicación, en nuestro caso ‘../spec/mocks/response/example’, ahora debemos definir cada una de las respuestas en dicho directorio:

Para el success:

y para el error:

Ahora lo único que nos falta es hacer uso de los mocks en nuestro test así no tenemos inconvenientes con la request al exterior. Para hacerlo debemos requerir el archivo del mock que creamos previamente e invocar a cada método del mock donde esperamos una respuesta en particular:

Y con esto hecho, ya no solo no se sale a buscar la url verdadera, sino que también hemos organizado nuestro código, de una forma que nos permitiría manejar llamados a APIs grandes con muchos tipos de requests y respuestas.

Si te gustó este artículo, capaz podría interesarte alguno de estos también: