Quizás este no es un artículo propiamente de Go pero si que utilizaremos una herramienta escrita en Go y además creada por nosotros, que nada tiene que envidiar a otras soluciones de mock server del mercado.
¿Qué es un mock server?
Seguramente mientras estamos desarrollando nos hemos encontrado con la necesidad de conectar con una API de terceros o algún microservicio propio para poder hacer alguna funcionalidad, poned por ejemplo que tenemos que conectarnos con la API que ya desarrollamos en un artículo anterior, GopherAPI.
Al estar conectados a una API real si queremos hacer pruebas se nos puede hacer complicado, ya que puede que tengamos restricciones como sólo hacer x peticiones al días, autenticaciones, o incluso procesos de compra que no podemos ir probando sin parar, o podría ser que podamos consumir de dicho microservicio pero lo lleve otro equipo y haya subido un bug y tengamos que esperar a que lo arreglen, o no poder empezar a desarrollar por ejemplo el frontend de la aplicación por no tener un server sobre el que probar. Seguro que son situaciones que os habéis encontrado en vuestro día a día, ¿pero cómo lo solucionamos?
Imagino que no os pillaré por sorpresa si os digo que con un mock server, y ¿qué es un mock server? pues nada más y nada menos que un servidor que provee datos dummy para simular dependencias externas
¿Por qué debería de utilizar un mock server?
Como ya hemos comentado nos podemos encontrar multitud de situaciones que hagan nuestro día a día desarrollando algo frustrante, pero además de esto podemos encontrar otras situaciones donde un mock server nos será de gran utilidad.
Primero que nada cuando utilizamos un mock server incluimos el mismo dentro de nuestro proyecto, con lo cual obtenemos una de los primeros beneficios de utilizar un mock server que es tenerlo todo auto contenido es decir nuestro proyecto podrá ejecutarse de manera aislada.
Gracias a esto además todo estará desacoplado de dependencias externas, por ello da igual que si nuestro proyecto depende de un proyecto de otro equipo y este otro equipo sube un bug no nos debe afectar durante nuestro proceso de desarrollo, aunque si que habrá que tener una buena suite de tests que nos cubran de estos casos antes de subir a producción.
Finalmente lo que obtendremos es un entorno de desarrollo realmente estable.
Otra ventaja que iremos observando al utilizar un mock server es que al ser un server construido a partir de ciertos ficheros de configuración, no necesitará de ninguna dependencia externa, esto hará que sea un servicio muy liviano y rápido, es decir no ocupará muchos recursos de tu máquina durante el desarrollo, además al ser ejecutado en localhost no tendremos problemas de latencia o de comunicación con otros microservicios.
Y por último y quizás un punto realmente importante a tener en cuenta es el de testing y es que con un mock server tenemos el control absoluto de lo que sucede en nuestro servidor por ello podemos llegar a crear casos que son muy complejos de replicar en un entorno real.
Me has convencido, ¿cómo creo mi mock server?
Como comentamos al principio del artículo para crear nuestro mock server vamos a utilizar una aplicación que hemos desarrollado desde Friends of Go y si nos sigues desde hace tiempo posiblemente la conozcas de sobra, no es otra que Killgrave, llamada así por el villano de marvel que era capaz de que las personas hicieran lo que él quisiera con sólo decirlo, igual que un mock server. Además la aplicación es completamente Open Source y se aceptan de muy buen grado contribuciones.
Habría que remarcar que hay empresas como Atrápalo que ya la utilizan en su día a día y les ha ayudado a mejorar su flujo de trabajo.
Lo primero que debes de hacer para empezar a utilizar la aplicación es descargarla e instalarla, y tenemos varios modos de hacer esto:
Si eres usuario de Mac y tienes Homebrew instalado es tan sencillo como:
$ brew install friendsofgo/tap/killgrave
O puedes compilarla desde Go:
$ go get -u github.com/friendsofgo/killgrave/cmd/killgrave
O vía docker:
$ docker run -it --rm -p 3000:3000 -v $PWD/:/home -w /home friendsofgo/killgrave
O incluso instalar el binario que más se adapte a tus necesidades y plataforma, https://github.com/friendsofgo/killgrave/releases.
Una vez instalado tendremos a nuestra disposición las siguientes opciones:
-config string
path with configuration file
-host string
if you run your server on a different host (default "localhost")
-imposters string
directory where your imposter are saved (default "imposters")
-port int
por to run the server (default 3000)
-version
show the version of the application
Configurando nuestro mock server
Vamos a aprovechar la API que realizamos en su día, GopherAPI para hacer el mock de la misma, recordemos que dentro del propio repositorio tenemos la colección de Postman que nos puede ir de maravilla para ver como funcionan los endpoints.
Dado que Killgrave funciona con ficheros de configuración y es mucho más cómodo que estar poniendo cada parámetro por flag, empezaremos creando dicho fichero de configuración.
# config.yml
imposters_path: "gopherapi"
port: 3000
host: "localhost"
cors:
methods: ["GET"]
headers: ["Content-Type"]
exposed_headers: ["Cache-Control"]
origins: ["*"]
allow_credentials: true
Vamos a explicar que es cada parámetro, imposters_path
aquí indicaremos donde estarán nuestros impostores, es decir dónde estarán los ficheros que le dirán a nuestros endpoints como deben de comportarse. Luego podemos especificar un puerto, port
, y un host
, además si el proyecto que va a consumir de tu mock server es un proyecto javascript, podrás configurar el cors
, siendo este parámetro completamente opcional y podríamos obviarlo.
¿Cómo creamos un imposter?
Vamos a ver un ejemplo completito como es la creación de un Gopher, que se trata de un POST
al endpoint /gophers
donde además tendremos que pasar un request body en json
.
Vamos a ver cómo quedaría nuestro imposter
y luego explicaremos cada parte.
# gopherapi/create_gopher.imp.json
[
{
"request": {
"method": "POST",
"endpoint": "/gophers",
"schemaFile": "schemas/create_gopher_request.json",
"headers": {
"Content-Type": "application/json"
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
}
}
}
]
Antes que nada hay que aclarar una cosa, cada fichero de imposter, debe ser del tipo .imp.json
y no sólo .json
, esta ha sido una extensión que decidimos crear de cara a que pudieran haber otro tipos de ficheros al mismo nivel y no hubiera que estar comprobando si son imposters o no.
Algo muy importante de los imposters
es que un mismo fichero puede contener N imposters
, esto es muy útil para poder simular diferentes casos, sólo hay que tener en cuenta que debemos mantener nuestros imposters
ordenados, empezando por el más restrictivo y acabando por el menos restrictivo.
Por ejemplo supongamos que enviando un query param error=true
queremos hacer que devuelva un error 500, pero en los demás casos siga respondiendo igual.
# gopherapi/create_gopher.imp.json
[
{
"request": {
"method": "POST",
"endpoint": "/gophers",
"schemaFile": "schemas/create_gopher_request.json",
"headers": {
"Content-Type": "application/json"
},
"params": {
"error": "true"
}
},
"response": {
"status": 500,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"error\": \"gopher could not be saved\"}"
}
},
{
"request": {
"method": "POST",
"endpoint": "/gophers",
"schemaFile": "schemas/create_gopher_request.json",
"headers": {
"Content-Type": "application/json"
},
"params": {
"apiKey": "{v:[\\w]+}"
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
}
}
}
]
Colocando primero el caso más restrictivo, haremos que sea el primero en ser evaluado, si pusiéramos el menos restrictivo entraríamos en ese caso primeramente y nunca llegaríamos a ver el error.
Algo que tenéis que conocer además de los imposters
es que cuando hagamos una modificación sobre ellos deberéis de reiniciar la aplicación.
Configurando mi Request
Ya hemos visto como crear un imposters
pero vamos a entrar en detalle en cada parte, empezando por la request
.
Lo primero que vemos es method
, obviamente aquí podremos definir el tipo de método que esperamos en nuestra request
, en este caso como dijimos será un POST
, después tendremos que decidir que endpoint
queremos simular, tendremos que poner la ruta relativa al host
, en nuestro caso declaramos como host
, localhost
y 3000
como puerto, así que cuando hagamos una petición a este endpoint tendremos que hacerlo con la url, http://localhost:3000/gophers
.
A continuación nos encontramos con una funcionalidad propia de Killgrave
y realmente interesante y es que podemos definir un json schema para validar nuestra request
, gracias a esto no tendremos que especificar campos especificos, sino que podremos decir, que esperamos cierto campo de tipo string, boolean, etc, o ¡incluso regex!, así que tendréis un sin fin de posibilidades para poder validar vuestras requests.
En nuestro caso el json schema
a utilizar será el siguiente:
# gopherapi/schemas/create_gopher_request.json
{
"type": "object",
"properties": {
"ID": {
"type": "string"
},
"name": {
"type": "string"
},
"image": {
"type": "string"
},
"age": {
"type": "integer"
}
},
"required": [
"ID",
"name",
"image",
"age"
]
}
Este parámetro además es opcional, ya que nuestras peticiones no siempre tienen que venir con un body.
Por otro lado nos encontramos con dos parámetros opcionales más que son, headers
, y params
ambos son parámetros opcionales pero que una vez establecidos son de cumplimiento obligatorio, es decir si establecemos que nuestra request
reciba el header
, Content-Type
con el valor application/json
, cuando hagamos la petición POST
debemos de pasar dicho header
de lo contrario nos devolvería un 404
, lo mismo pasa con los params
, los cuales se usan para definir los parámetros de una url, por ejemplo ?apiKey=12345678
.
Ambos parámetros permiten el uso de regex, pero con ciertas particularidades si queremos definir una regex
en un header haremos lo siguiente:
...
"headers": {
"Content-Type": "application/json",
"Api-Key": "[\\w]+"
}
...
Esto nos obligaría para cumplir con este imposter
a tener que pasar un header Api-Key
con una palabra. Por el contrario podemos querer por ejemplo pasar el apikey
vía query param
...
"headers": {
"Content-Type": "application/json"
}
"params": {
"apiKey": "{v:[\\w]+}"
}
...
Como veis aquí la notación cambia un poco, y es que cuando vamos a usar regex
dentro de los endpoints
o de los params
debemos hacerlo con la notación que específica Gorilla Mux que es la librería que usa Killgrave
por debajo para funcionar, que no es otra que poner la regex entre llaves y asignando un nombre de variable seguido de dos puntos.
Con esto ya tendríamos nuestra request
lista, pero como hemos comentado antes, un mock server simula las respuestas que queremos obtener a partir de una request
, así que veamos como configuramos nuestra response.
Configurando mi Response
La parte de la response es todavía más sencilla si cabe, en nuestro ejemplo anterior en el caso satisfactorio veíamos una `response como esta:
...
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
}
}
...
Donde podemos definir el status
, el cual corresponderá con cualquier código http válido, además podemos especificar que headers
va a devolver nuestra respuesta, incluso podemos llegar a especificar si lo que vamos a devolver es un html
, csv
, pdf
, etc…
Si además vemos el ejemplo que pusimos cuando devolvíamos error
...
"response": {
"status": 500,
"headers": {
"Content-Type": "application/json"
},
"body": "{\"error\": \"gopher could not be saved\"}"
}
...
Podemos especificar un body
, o si lo que vamos a retornar es algún fichero o el json
es demasiado engorroso para tenerlo en el propio imposter, podemos cambiar este argumento a bodyFile
y especificar la ruta de nuestro fichero de response
.
Resumen
Ya estáis listos para empezar a crear vuestros mock server y ponerlo como una tarea más de vuestro desarrollo, para poder tener todas las ventajas que ofrecen, si llegáis a empezar a utilizar Killgrave
en vuestra empresa dejádnoslo saber, enviándonos un mail a contact@friendsofgo.tech estaremos muy contento de saber sobre ello. Además recordad que es un proyecto Open Source, escrito íntegramente en Go, con lo cual puede ser un buen sitio para empezar a colaborar.
Y como siempre, si tenéis cualquier duda o comentario podéis dejarlo en los comentarios o en nuestro Twitter @FriendsofGoTech.