Aquí tienes la serie completa de tutoriales sobre Mountebank:
- Mountebank – Customizando la configuración
- Mountebank – Jugando con los predicados
- Mountebank – Respuestas enlatadas (este tutorial)
- Mountebank – Respuestas proxy
- Mountebank – Respuestas inject
- Mountebank – Comportamientos
Entorno
El tutorial está escrito usando el siguiente entorno:
- Hardware: Portátil MacBook Pro 15′ (2,3 Ghz Intel Core i7, 16GB DDR4).
- Sistema Operativo: Mac OS Catalina 10.15.5
- Entorno de desarrollo: JDK 11, IntelliJ, Docker, Postman
Introducción
En este artículo vamos a ver el uso de uno de los 3 tipos de respuestas que provee Mountebank, las respuestas de tipo is o enlatadas, espero que os guste y/o sirva de ayuda en vuestros tests.
Este artículo está dividido en 7 partes:
- ¿Qué son las respuestas?
- Tipos de respuestas
- Respuestas de tipo is
- Respuesta por defecto
- Respuestas infinitas
- Impostores HTTPS
- Conclusiones
¿Qué son las respuestas?
Como su propio nombre indica se trata de las respuestas que Mountebank va a devolver cuando la solicitud coincida con todos los predicados del array tal y como vimos en el anterior tutorial.
Mountebank se encarga de traducir la estructura de respuesta de los datos de JSON al formato de red esperado por el sistema bajo prueba, el cual se indica en el campo protocolo del impostor.
Tipos de respuestas
Mountebank tiene tres tipos de respuesta diferentes
- Un tipo de respuesta is que devuelve el JSON proporcionado, creando una respuesta que se denomina “enlatada”
- Un tipo de respuesta proxy que reenvía la solicitud a una dependencia real y convierte su respuesta en una estructura de respuesta JSON.
- Un tipo de respuesta de inyección que permite definir programáticamente la respuesta JSON utilizando JavaScript. La inyección es la forma en que se puede extender mountebank cuando sus capacidades incorporadas no hacen exactamente lo que se necesita
Respuestas de tipo is
El tipo de respuesta is, es el componente fundamental de un stub, se trata de respuestas “estáticas” que devuelven el contenido tal cual se define en el stub.
Vamos a ver un ejemplo sencillo de una respuesta enlatada, el típico Hola mundo, donde definimos un impostor con una única respuesta sin predicado, por lo que cualquier llamada que hagamos al servicio virtual siempre nos va a devolver el mismo resultado.
{ "protocol": "http", "port": 80, "stubs": [ { "responses": [ { "is": { "statusCode": 200, "headers": { "ContentType": "text/plain" }, "body": "Hello, world!" } } ] } ] }
La respuesta es casi, pero no del todo, la misma que la respuesta «Hello, world» que se muestra a continuación:
HTTP/1.1 200 OK ContentType: text/plain Connection: close Date: Wed, 21 Jun 2020 01:42:38 GMT TransferEncoding: chunked Hello, world!
Tres cabeceras HTTP adicionales de alguna manera se colaron, entender de dónde proceden estas cabeceras nos obliga a revisar otras características de las respuestas, como la respuesta por defecto.
Respuesta por defecto
Mountebank utiliza un stub oculto por defecto cuando la solicitud no coincide con ningún predicado. Ese stub predeterminado no contiene predicados, por lo que siempre coincide con la solicitud, y contiene exactamente una respuesta: la respuesta por defecto. Podemos ver esta respuesta predeterminada si creamos un impostor sin stubs y posteriormente enviamos una petición HTTP a ese puerto.
Mountebank fusiona la respuesta predeterminada en cualquier respuesta que proporcionamos, qué quiere decir esto, significa que sólo se necesita especificar los campos que son diferentes de los predeterminados, simplificando la configuración de la respuesta.
Mountebank nos permite cambiar la respuesta predeterminada para que se adapte mejor a nuestras necesidades, como podemos ver en el ejemplo, donde hemos configurado en la respuesta predeterminada además del statusCode los encabezados Connection y ContentLenght, con Connection el servidor le dice al cliente que mantenga abierta la conexión ya que Mountebank lo cierra por defecto
El comportamiento predeterminado del impostor establece el encabezado TransferEncoding: chunked, que divide el cuerpo en una serie de trozos y prefijos, cada uno con el número de bytes que contiene. La ventaja de enviar al cuerpo un trozo cada vez es que el servidor puede comenzar a transmitir datos al cliente antes de que el servidor tenga todos los datos. La estrategia alternativa es calcular la longitud de todo el cuerpo HTTP antes de enviarlo y proporcionar esa información en el encabezado.
{ ... "defaultResponse": { "statusCode": 400, "headers": { "Connection": "KeepAlive", "ContentLength": 0 } }, "stubs": [ { "predicates": [{...}], "responses": [{...}] } ] }
Respuestas infinitas
Otra característica que provee Mountebank en las respuestas son las respuestas infinitas, vamos a verlas con otro ejemplo, imaginemos que tenemos un escenario de prueba de petición de pedidos donde parte del proceso de envío de pedidos consiste en comprobar que el inventario es suficiente, ya que el inventario no es estático, se vende y se repone, necesitamos que la misma solicitud al servicio de inventario pueda responder un resultado diferente cada vez.
{ "protocol": "http", "port": 3000, "stubs": [ { "responses": [ { {"is": {"body": "54"}}, {"is": {"body": "21"}}, {"is": {"body": "0"}} } ] } ] }
Creando un impostor como se muestra en el ejemplo, podemos ver que la primera llamada devuelve 54, la segunda devuelve 21, y la tercera devuelve 0. Si se necesitará realizar una cuarta llamada, volverá a devolver 54, luego 21, y 0 y así sucesivamente. Mountebank trata la lista de respuestas como una lista infinita, con la primera y última entrada conectadas como un círculo.
Impostores HTTPS
La creación de un impostor HTTPS se parece a la creación de un impostor HTTP. La única diferencia es que el protocolo se establece en https. Esto es ideal para configurar rápidamente un servidor HTTPS, pero utiliza un certificado predeterminado, ese certificado es a la vez inseguro y no confiable.
Mountebank permite crear el impostor con el certificado y la clave privada, para ello basta con especificar los parámetros key y cert en lo que se conoce como formato PEM
Establecer el indicador mutualAuth en un impostor significa que aceptará los certificados de cliente utilizados para la autenticación.
{ "protocol": "https", "port": 443, "key": "BEGIN RSA PRIVATE KEYnMIIEpAIBAAKC...", "cert": "BEGIN CERTIFICATEnMIIDejCCAmICCQD...", "mutualAuth": true }
Mountebank utiliza un lenguaje de plantillas llamado EJS para persistir en la configuración de los impostores y ha incorporado algunas mejoras que nos permiten simplificar y hacer más legibles las plantillas, como veis en el ejemplo definir el certificado de esta forma ni es legible ni fácil ni cómodo.
Mountebank agrega la función stringify al lenguaje de plantillas, que hace el equivalente a una llamada JSON.stringify de JavaScript sobre el contenido del archivo dado, convirtiendo el contenido del archivo multilínea en una cadena JSON haciendo mucho más legible nuestra plantilla EJS.
{ "port": 80, "cert": "<% stringify(filename, 'ssl/cert.pem') %>", "key": "<% stringify(filename, 'ssl/key.pem') %>", "stubs": [ { "predicates": [{...}], "responses": [{...}] } ] }
Conclusiones
En este artículo hemos repasado qué son y cómo podemos utilizar los tipos de respuesta is o enlatadas que proporciona Mountebank para devolver nuestras respuestas a las llamadas que hagan nuestros test, también hemos visto que es la respuesta por defecto, como crear respuestas infinitas y por último como configurar rápidamente un servidor HTTPS.
Puedes descargar el proyecto completo aquí.
Referencias
https://github.com/bbyars/mountebank
https://github.com/bbyars/mountebank-in-action
https://hub.docker.com/r/bbyars/mountebank
http://www.mbtest.org/docs/api/stubs