Una guía para descansar asegurado

Jackson Top

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO

1. Introducción

REST-secure fue diseñado para simplificar las pruebas y la validación de las API REST y está muy influenciado por las técnicas de prueba utilizadas en lenguajes dinámicos como Ruby y Groovy.

La biblioteca tiene un soporte sólido para HTTP, comenzando por supuesto con los verbos y las operaciones HTTP estándar, pero también yendo mucho más allá de estos conceptos básicos.

En esta guía, exploraremos la seguridad de REST y usaremos Hamcrest para hacer afirmaciones. Si aún no está familiarizado con Hamcrest, primero debe repasar el tutorial: Pruebas con Hamcrest.

Además, para obtener información sobre casos de uso más avanzados de REST asegurado, consulte nuestros otros artículos:

  • DESCANSO asegurado con Groovy
  • Validación de esquema JSON con REST asegurado
  • Parámetros, encabezados y cookies con REST asegurado

Ahora vamos a sumergirnos en un ejemplo sencillo.

2. Prueba de ejemplo simple

Antes de comenzar, asegurémonos de que nuestras pruebas tengan las siguientes importaciones estáticas:

io.restassured.RestAssured.* io.restassured.matcher.RestAssuredMatchers.* org.hamcrest.Matchers.*

Lo necesitaremos para simplificar las pruebas y tener fácil acceso a las API principales.

Ahora, comencemos con un ejemplo simple: un sistema de apuestas básico que expone algunos datos para juegos:

{ "id": "390", "data": { "leagueId": 35, "homeTeam": "Norway", "visitingTeam": "England", }, "odds": [{ "price": "1.30", "name": "1" }, { "price": "5.25", "name": "X" }] }

Digamos que esta es la respuesta JSON al acceder a la API implementada localmente - // localhost: 8080 / events? Id = 390. :

Usemos ahora REST-secure para verificar algunas características interesantes de la respuesta JSON:

@Test public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() { get("/events?id=390").then().statusCode(200).assertThat() .body("data.leagueId", equalTo(35)); }

Entonces, lo que hicimos aquí es: verificamos que una llamada al endpoint / events? Id = 390 responde con un cuerpo que contiene una cadena JSON cuyo leagueId del objeto de datos es 35.

Echemos un vistazo a un ejemplo más interesante. Digamos que le gustaría verificar que la matriz de probabilidades tenga registros con precios 1.30 y 5.25 :

@Test public void givenUrl_whenJsonResponseHasArrayWithGivenValuesUnderKey_thenCorrect() { get("/events?id=390").then().assertThat() .body("odds.price", hasItems("1.30", "5.25")); }

3. Configuración asegurada por REST

Si su herramienta de dependencia favorita es Maven, agregamos la siguiente dependencia en el archivo pom.xml :

 io.rest-assured rest-assured 3.3.0 test 

Para obtener la última versión, siga este enlace.

REST-secure aprovecha el poder de los comparadores de Hamcrest para realizar sus afirmaciones, por lo que también debemos incluir esa dependencia:

 org.hamcrest hamcrest-all 2.1 

La última versión siempre estará disponible en este enlace.

4. Validación de raíz JSON anónima

Considere una matriz que se compone de primitivas en lugar de objetos:

[1, 2, 3]

Esto se denomina raíz JSON anónima, lo que significa que no tiene un par clave-valor, sin embargo, sigue siendo datos JSON válidos.

Podemos ejecutar la validación en tal escenario usando el $símbolo o una Cadena vacía (“”) como ruta. Supongamos que exponemos el servicio anterior a través de // localhost: 8080 / json, luego podemos validarlo de esta manera con REST asegurado:

when().get("/json").then().body("$", hasItems(1, 2, 3));

o así:

when().get("/json").then().body("", hasItems(1, 2, 3));

5. Flotadores y dobles

Cuando comenzamos a usar REST-secure para probar nuestros servicios REST, debemos comprender que los números de punto flotante en las respuestas JSON se asignan al tipo flotante primitivo .

El uso del tipo float no es intercambiable con double como es el caso de muchos escenarios en java.

El caso en cuestión es esta respuesta:

{ "odd": { "price": "1.30", "ck": 12.2, "name": "1" } }

supongamos que estamos ejecutando la siguiente prueba con el valor de ck :

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));

Esta prueba fallará incluso si el valor que estamos probando es igual al valor en la respuesta. Esto se debe a que estamos comparando con un doble en lugar de un flotador .

To make it work, we have to explicitly specify the operand to the equalTo matcher method as a float, like so:

get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));

6. Specifying the Request Method

Typically, we would perform a request by calling a method such as get(), corresponding to the request method we want to use.

In addition, we can also specify the HTTP verb using the request() method:

@Test public void whenRequestGet_thenOK(){ when().request("GET", "/users/eugenp").then().statusCode(200); }

The example above is equivalent to using get() directly.

Similarly, we can send HEAD, CONNECT and OPTIONS requests:

@Test public void whenRequestHead_thenOK() { when().request("HEAD", "/users/eugenp").then().statusCode(200); }

POST request also follows a similar syntax and we can specify the body by using the with() and body() methods.

Therefore, to create a new Odd by sending a POST request:

@Test public void whenRequestedPost_thenCreated() { with().body(new Odd(5.25f, 1, 13.1f, "X")) .when() .request("POST", "/odds/new") .then() .statusCode(201); }

The Odd object sent as body will automatically be converted to JSON. We can also pass any String that we want to send as our POSTbody.

7. Default Values Configuration

We can configure a lot of default values for the tests:

@Before public void setup() { RestAssured.baseURI = "//api.github.com"; RestAssured.port = 443; }

Here, we're setting a base URI and port for our requests. Besides these, we can also configure the base path, root pat, and authentication.

Note: we can also reset to the standard REST-assured defaults by using:

RestAssured.reset();

8. Measure Response Time

Let's see how we can measure the response time using the time() and timeIn() methods of the Response object:

@Test public void whenMeasureResponseTime_thenOK() { Response response = RestAssured.get("/users/eugenp"); long timeInMS = response.time(); long timeInS = response.timeIn(TimeUnit.SECONDS); assertEquals(timeInS, timeInMS/1000); }

Note that:

  • time() is used to get response time in milliseconds
  • timeIn() is used to get response time in the specified time unit

8.1. Validate Response Time

We can also validate the response time – in milliseconds – with the help of simple longMatcher:

@Test public void whenValidateResponseTime_thenSuccess() { when().get("/users/eugenp").then().time(lessThan(5000L)); }

If we want to validate the response time in a different time unit, then we'll use the time() matcher with a second TimeUnit parameter:

@Test public void whenValidateResponseTimeInSeconds_thenSuccess(){ when().get("/users/eugenp").then().time(lessThan(5L),TimeUnit.SECONDS); }

9. XML Response Verification

Not only can it validate a JSON response, itcan validate XML as well.

Let's assume we make a request to //localhost:8080/employees and we get the following response:

  Jane Daisy f  

We can verify that the first-name is Jane like so:

@Test public void givenUrl_whenXmlResponseValueTestsEqual_thenCorrect() { post("/employees").then().assertThat() .body("employees.employee.first-name", equalTo("Jane")); }

We can also verify that all values match our expected values by chaining body matchers together like so:

@Test public void givenUrl_whenMultipleXmlValuesTestEqual_thenCorrect() { post("/employees").then().assertThat() .body("employees.employee.first-name", equalTo("Jane")) .body("employees.employee.last-name", equalTo("Daisy")) .body("employees.employee.sex", equalTo("f")); }

Or using the shorthand version with variable arguments:

@Test public void givenUrl_whenMultipleXmlValuesTestEqualInShortHand_thenCorrect() { post("/employees") .then().assertThat().body("employees.employee.first-name", equalTo("Jane"),"employees.employee.last-name", equalTo("Daisy"), "employees.employee.sex", equalTo("f")); }

10. XPath for XML

We can also verify our responses using XPath. Consider the example below that executes a matcher on the first-name:

@Test public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() { post("/employees").then().assertThat(). body(hasXPath("/employees/employee/first-name", containsString("Ja"))); }

XPath also accepts an alternate way of running the equalTo matcher:

@Test public void givenUrl_whenValidatesXmlUsingXpath2_thenCorrect() { post("/employees").then().assertThat() .body(hasXPath("/employees/employee/first-name[text()='Jane']")); }

11. Logging Test Details

11.1. Log Request Details

First, let's see how to log entire request details using log().all():

@Test public void whenLogRequest_thenOK() { given().log().all() .when().get("/users/eugenp") .then().statusCode(200); }

This will log something like this:

Request method: GET Request URI: //api.github.com:443/users/eugenp Proxy:  Request params:  Query params:  Form params:  Path params:  Multiparts:  Headers: Accept=*/* Cookies:  Body: 

To log only specific parts of the request, we have the log() method in combination with params(), body(), headers(), cookies(), method(), path() eg log.().params().

Note that other libraries or filters used may alter what's actually sent to the server, so this should only be used to log the initial request specification.

11.2. Log Response Details

Similarly, we can log the response details.

In the following example we're logging the response body only:

@Test public void whenLogResponse_thenOK() { when().get("/repos/eugenp/tutorials") .then().log().body().statusCode(200); }

Sample output:

{ "id": 9754983, "name": "tutorials", "full_name": "eugenp/tutorials", "private": false, "html_url": "//github.com/eugenp/tutorials", "description": "The \"REST With Spring\" Course: ", "fork": false, "size": 72371, "license": { "key": "mit", "name": "MIT License", "spdx_id": "MIT", "url": "//api.github.com/licenses/mit" }, ... }

11.3. Log Response if Condition Occurred

We also have the option of logging the response only if an error occurred or the status code matches a given value:

@Test public void whenLogResponseIfErrorOccurred_thenSuccess() { when().get("/users/eugenp") .then().log().ifError(); when().get("/users/eugenp") .then().log().ifStatusCodeIsEqualTo(500); when().get("/users/eugenp") .then().log().ifStatusCodeMatches(greaterThan(200)); }

11.4. Log if Validation Failed

We can also log both request and response only if our validation failed:

@Test public void whenLogOnlyIfValidationFailed_thenSuccess() { when().get("/users/eugenp") .then().log().ifValidationFails().statusCode(200); given().log().ifValidationFails() .when().get("/users/eugenp") .then().statusCode(200); }

En este ejemplo, queremos validar que el código de estado es 200. Solo si esto falla, se registrarán la solicitud y la respuesta.

12. Conclusión

En este tutorial, hemos explorado el marco asegurado por REST y analizado sus características más importantes que podemos usar para probar nuestros servicios RESTful y validar sus respuestas.

La implementación completa de todos estos ejemplos y fragmentos de código se puede encontrar en el proyecto de GitHub asegurado por REST.

Jackson fondo

Acabo de anunciar el nuevo curso Learn Spring , centrado en los fundamentos de Spring 5 y Spring Boot 2:

>> VER EL CURSO