Mountebank – Jugando con los predicados

0
583

Aquí tienes la serie completa de tutoriales sobre Mountebank:

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 predicados para enviar diferentes respuestas a diferentes solicitudes, simplificación de predicados en cuerpos de peticiones JSON y por último vamos a usar JSONPath y XPath para simplificar los predicados en los cuerpos de peticiones XML, espero que os guste y/o sirva de ayuda en vuestros tests.

Este artículo está dividido en 4 partes:

 

¿Qué son los predicados?

Los predicados permiten a los impostores tener un comportamiento mucho más rico definiendo si un stub coincide o no con una petición, esto quiere decir que predicates permite que mountebank responda de manera diferente ante diferentes solicitudes.

Cualquier campo de la petición HTTP se puede utilizar en los predicados: method, path, query y headers, además el campo predicates es un array, esto quiere decir que todos los predicados de la matriz deben coincidir para que Mountebank use ese stub, debido a que una solicitud puede coincidir con varios stubs, mountebank escogerá la primera coincidencia, siempre basado en el orden de la matriz.

Todos los predicados son insensibles a mayúsculas y minúsculas por defecto, pero Si se necesita sensibilidad a mayúsculas y minúsculas, se puede ajustar el parámetro caseSensitive a true. 

 

Tipos de predicados

La siguiente tabla proporciona la lista completa de los operadores predicados que mountebank soporta:

Operador Descripción
equals Requiere que el campo de petición sea igual al valor predicado
deepEquals Realiza igualdad de conjuntos anidados en los campos de solicitud de objetos
contains Requiere que el campo de petición contenga el valor predicado
startsWith Requiere que el campo de petición comience con el valor predicado
endsWith Requiere que el campo de petición termine con el valor predicado
Matches Requiere que el campo de petición coincida con la expresión regular proporcionada como valor predicado
exists Requiere que el campo de solicitud exista como un valor no vacío (si es verdadero) o no (si es falso).
not Invierte el subpredicador
or Requiere que se cumpla cualquiera de los subprevistos
and Requiere que todos los subpredicadores estén satisfechos
inject Requiere una función proporcionada por el usuario para devolver true

Destacar de entre los tipos de predicados de Mountebank, el predicado deepEquals ya que es el único que no solo trabaja en un campo de la petición, sino con estructuras de pares de valores clave más complejas, como por ejemplo objetos, además difiere del predicado equals en que para que el predicado deepEquals es el único que requiere una coincidencia exacta. 

Vamos a ver que quiero decir con un ejemplo, suponemos que tenemos el siguiente impostor:

{
    ...
    "stubs": [
        {
            "predicates": [
                {
                    "deepEquals": {
                        "query": {
                            "q": [
                                "BASIC",
                                "INTERMEDIATE"
                            ]
                        }
                    }
                }
            ],
            "responses": [
                {
                    "is": {
                        "body": "deepEquals matched"
                    }
                }
            ]
        },
        {
            "predicates": [
                {
                    "equals": {
                        "query": {
                            "q": [
                                "BASIC",
                                "INTERMEDIATE"
                            ]
                        }
                    }
                }
            ],
            "responses": [
                {
                    "is": {
                        "body": "equals matched"
                    }
                }
            ]
        }
    ]
}

Si enviamos una petición a /courses?q=INTERMEDIATE&q=BASIC, el cuerpo de la respuesta mostrará que el predicado deepEquals coincide porque todos los elementos del array coinciden y no hay elementos adicionales en la petición. Pero si envía una petición a /courses?q=INTERMEDIATE&q=BASIC&q=ADVANCED, el predicado de deepEquals ya no coincidirá porque la definición de predicado no espera al «ADVANCED» como elemento de la matriz. El predicado equals coincidirá, porque permite elementos adicionales en la matriz de peticiones que no están especificados en la definición del predicado.

Y el predicado matches que es uno de los predicados más versátiles de mountebank, ya que con un par de metacaracteres adicionales puede reemplazar completamente a cualquiera de los otros predicados, la siguiente tabla muestra ejemplos de metacaracteres soportados por Mountebank

Metacharacter Descripción Ejemplo
A menos que forme parte de un metacaracter como los que se describen a continuación, escapa al siguiente personaje, forzando una coincidencia literal. 4 * 2? matches “What is 4 * 2?”
^ Coincide con el principio de la cadena ^Hello matches “Hello, World!” butnot “Goodbye. Hello.”
$ Coincide con el final de la cadena World!$ matches “Hello, World!”but not “World! Hello.”
. Coincide con cualquier carácter que no sea de línea nueva …. matches “Hello” but not “Hi”
* Coincide con el carácter anterior 0 o más veces a*b matches “b” and “ab” and “aaaaaab” 
? Coincide con el carácter anterior 0 ó 1 vez a?b matches “b” and “ab” but not “aab”
+ Coincide con el carácter anterior 1 o más veces a+b matches “ab” and “aaaab” but not “b”
d Coincide con un dígito ddd matches “123” but not “12a”
D Invierte los caracteres que no coinciden con los dígitos DDD matches “ab!” but not “123”
w Concuerda con un carácter alfanumérico de «word» www matches “123” and “abc” but not “ab!”
W Invierte, emparejando pero nosímbolos no alfanuméricos WWW matches “!?.” but not “ab.”
s Coincide con un carácter de espacio en blanco (principalmente espacios, tabulaciones y líneas nuevas). Hellosworld matches “Hello world” and “Hello world”
S Invierte, haciendo coincidir cualquier carácter no espacial HelloSworld matches “Hello-world” and “Hello­­­­world”

Las expresiones regulares permiten definir patrones robustos para que coincidan con los caracteres de la solicitud.

 

JSONPath y XPath

Mountebank trata los cuerpos HTTP como JSON y haciendo uso de JSONPath y XPath para llamadas en formato XML, nos permite navegar por la estructura de objetos tantos niveles como se necesite para seleccionar los valores del documento que nos interese como podemos ver en el siguiente ejemplo, esto nos da versatilidad a la hora de trabajar con estructuras de datos grandes y/o con muchos niveles.

Los parámetros predicados jsonpath y xpath limitan el alcance en el campo de petición a la parte que coincide con el selector JSONPath o XPath.

Vamos a ver a continuación ejemplos de cada uno de ellos, en ambos casos partimos de una petición que inicializa el conjunto de cursos enviando un comando PUT a la ruta /courses, pasando una matriz de cursos, como se muestra aquí:

{
    "courses": [
        {
            "id": "e646a1dc-61a8-4324-8fd4-58f84328be5d",
            "name": "Integración de Spring con el envío de emails",
            "level": "INTERMEDIATE"
        },
        {
            "id": "d355ef6e-1f12-4731-9ae7-64a863aca822",
            "name": "Primeros pasos con github: subir un proyecto al repositorio.",
            "level": "BASIC"
        }
    ]
}

Este escenario de prueba requiere que se devuelva un 400 si el comando PUT incluye un curso «Ejb3 Timer Service: Scheduling» como último miembro del array y un 200 en caso contrario. Esto es obviamente un poco exagerado, pero me permite mostrar el poder de JSONPath. 

 

JSONPath

JSONPath es un lenguaje de consulta que simplifica la tarea de seleccionar valores de un documento JSON y sobresale con documentos grandes y complejos.

A continuación se muestra cómo sería el impostor para documentos en formato JSON.

{
    "protocol": "http",
    "port": 3000,
    "stubs": [
        {
            "predicates": [
                {
                    "equals": {                                             1
                        "method": "PUT"                                     1
                    }                                                       1
                },
                {
                    "equals": {                                             1
                        "path": "/courses"                                  1
                    }                                                       1
                },
                {
                    "jsonpath": {                                           2
                        "selector": "$.courses[(@.length­1)].name"           2
                    },                                                      2
                    "equals": {                                             3
                        "body": "Ejb3 Timer Service: Scheduling"            3
                    }                                                       3
                }
            ],
            "responses": [
                {
                    "is": {
                        "statusCode": 400                                   4
                    }
                }
            ]                                                               5
        }
    ]
}
  1. Sólo hace coincidir una petición PUT con /courses
  2. Limita el alcance del predicado a la consulta JSONPath
  3. El valor JSON seleccionado dentro del cuerpo debe ser igual a «Ejb3 Timer Service: Scheduling».
  4. Devuelve un código de estado de 400
  5. Devuelve la respuesta predeterminada 200 incorporada si el predicado no coincide

 

XPath

Aunque XML no es tan común en los servicios creados en los últimos años, sigue siendo un formato de servicio frecuente y se utiliza universalmente para los servicios SOAP, para el ejemplo en XML el cuerpo de la petición sería:

<courses>
    <course id="e646a1dc-61a8-4324-8fd4-58f84328be5d">
        <name>Integración de Spring con el envío de emails</name>
        <level>INTERMEDIATE</location>
    </course>
    <course id="d355ef6e-1f12-4731-9ae7-64a863aca822">
        <name>"Primeros pasos con github: subir un proyecto al repositorio.</name>
        <level>BASIC</location>
    </course>
</courses>

A continuación se muestra cómo sería el impostor para documentos en formato XML.

{
    "predicates": [
        {
            "equals": {                                         1
                "method": "PUT"                                 1
            }                                                   1
        },
        {
            "equals": {                                         1
                "path": "/courses"                              1
            }                                                   1
        },
        {
            "xpath": {                                          2
                "selector": "//course[last()]/name"             2
            },                                                  2
            "equals": {                                         3
                "body": "Ejb3 Timer Service: Scheduling"        3
            }                                                   3
        }
    ],
    "responses": [
        {
            "is": {                                             4
                "statusCode": 400                               4
            }                                                   4
        }
    ]                   
}
  1. Verifica que es un PUT to /courses
  2. Limita el predicado al valor dado
  3. El valor debe ser igual a «Ejb3 Timer Service: Scheduling.»
  4. Devuelve una solicitud errónea

 

Conclusiones

En este artículo hemos repasado qué son y cómo podemos utilizar los predicados de Mountebank para entrenar a nuestro servidor de tests de una forma mucho más eficiente.

Puedes descargar el proyecto completo aquí.

 

Referencias

http://www.mbtest.org/

https://github.com/bbyars/mountebank

https://github.com/bbyars/mountebank-in-action

https://hub.docker.com/r/bbyars/mountebank

https://goessner.net/articles/JsonPath/

https://www.w3.org/TR/1999/REC-xpath-19991116/

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad