QA

Automatizando Pruebas a Servicios REST con RestAssured y Cucumber

Publicado por
Jesidey Rincón
Automatizando Pruebas a Servicios REST con RestAssured y Cucumber

Muchas veces, la rutina de realizar pruebas repetitivas usando alguna herramienta para validar endpoints tiende a desgastarnos y pasar por “alto” bugs que dejamos escapar por probar que el servicio responda lo que debe responder, en pocas palabras el caso feliz.

Para aquellos proyectos o productos que siguen al ojo la pirámide de cohn es importante que sea automatizada este tipo de pruebas que son funcionales, pero en las cuales probamos solo el backend. La pregunta que sale luego de esto es, ¿lo estamos haciendo?, ¿estamos automatizando las pruebas funcionales a nivel de servicios?

Un pro de testear los servicios antes del frontend es que la volatidad es mínima, los cambios al final que se piden cuando tenemos un servicio terminado son bajas, caso distinto al frontend, que por lo general los requerimientos que siempre son pedidos apuntan a este. Para poder automatizar los servicios no es necesario probar casos bordes o cosas casi improbables que ocurran (estos casos dejémoslo a nuestro QA manual interno), lo que se busca con automatizar casos felices para los servicios, entre otras palabras, aplicar la propiedad KISS (keep is simply, stupid)

En el mercado hay múltiples herramientas para hacer pruebas manuales y también para automatizar, POSTMAN es un ejemplo y se pueden realizar los 2 tipos de pruebas. Lo que me gustaría compartir hoy con ustedes es RestAssured, una librería de java que nos permite realizar automated testing a servicios REST, es open source y tiene su repositorio Gitlab (https://github.com/rest-assured/rest-assured) en donde puedes dejar issues que serán atendidos.

En este artículo dejaré un ejemplo de cómo se puede automatizar un endpoint, teniendo la URL, body del request y una respuesta esperada. También usaré el framework de cucumber para escribir el test y sea de mejor entendimiento a la hora de ver los pasos que se llevan a cabo.

Ahora sí, manos a la obra

¿Qué tecnologías usaremos?

Primero, veamos primero el endpoint junto con el JSON que deseamos testear:

HTTP(POST) https://swapi.co/api/planets

{
   "name": "Yavin IV",
   "rotation_period": "24",
   "orbital_period": "4818",
   "diameter": "10200",
   "climate": "temperate, tropical",
   "gravity": "1 standard",
   "terrain": "jungle, rainforests",
   "surface_water": "8",
   "population": "1000"
}

Nuestro archivo pom.xml debe tener las siguientes dependencias para poder ejecutar los tests:

<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-java</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.cucumber</groupId>
<artifactId>cucumber-junit</artifactId>
<version>${cucumber.version}</version>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>


<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.9</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.hamcrest/hamcrest-core -->
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>

Crearemos nuestro feature con el scenario del caso que deseamos probar, en este ejemplo será la creación de un Planeta a través de un endpoint:

planet.feature

Feature: Planet feature example

 @CreatePlanetSuccesfull
 Scenario Outline: Create Star Wars Planet with a Service
   Given I have data to create a Planet with "<name>", "<rotation_period>", "<orbital_period>", "<diameter>", "<climate>", "<gravity>", "<terrain>", "<surface_water>" and "<population>"
   And I use planet header
   When I create post request to create a Planet
   Then I get status code 200 from db

   Examples:

     | name      | rotation_period | orbital_period  |  diameter |  climate  | gravity   | terrain     | surface_water | population  |
     | Kittie    | 24              | 4818            | 10200     | tropical  | standard  | rainforests | 8000          | 120         |

Así quedaría nuestro Gherkins, a partir de éste crearemos los métodos que corresponden a cada step. Para el paso 1 deberíamos construir el Objeto que luego se le enviará en un request:

PlanetStepDefinitions.java

@Given("I have data to create a Planet with {string}, {string}, {string}, {string}, {string}, {string}, {string}, {string} and {string}")
public void i_have_create_a_planet(String name, String rotation_period, String orbital_period, String diameter, String climate, String gravity, String terrain, String surface_water, String population){
   planet.setName(name);
   planet.setRotation_period(rotation_period);
   planet.setOrbital_period(orbital_period);
   planet.setDiameter(diameter);
   planet.setClimate(climate);
   planet.setGravity(gravity);
   planet.setTerrain(terrain);
   planet.setSurface_water(surface_water);
   planet.setPopulation(population);
}

Cabe destacar que antes de poder crear nuestro Objeto debe existir una clase que contenga los parámetros que conforman el JSON del ejemplo, y los métodos correspondientes (set y get):

StarWarsPlanet.java

package model;

public class StarWarsPlanet {

   private String orbital_period;

   private String surface_water;

   private String diameter;

   private String gravity;

   private String name;

   private String climate;

   private String rotation_period;

   private String terrain;

   private String population;

   public String getOrbital_period ()
   {
       return orbital_period;
   }

   public void setOrbital_period (String orbital_period)
   {
       this.orbital_period = orbital_period;
   }

   public String getSurface_water ()
   {
       return surface_water;
   }

   public void setSurface_water (String surface_water)
   {
       this.surface_water = surface_water;
   }

   public String getDiameter ()
   {
       return diameter;
   }

   public void setDiameter (String diameter)
   {
       this.diameter = diameter;
   }

   public String getGravity ()
   {
       return gravity;
   }

   public void setGravity (String gravity)
   {
       this.gravity = gravity;
   }

   public String getName ()
   {
       return name;
   }

   public void setName (String name)
   {
       this.name = name;
   }

   public String getClimate ()
   {
       return climate;
   }

   public void setClimate (String climate)
   {
       this.climate = climate;
   }

   public String getRotation_period ()
   {
       return rotation_period;
   }

   public void setRotation_period (String rotation_period)
   {
       this.rotation_period = rotation_period;
   }

   public String getTerrain ()
   {
       return terrain;
   }

   public void setTerrain (String terrain)
   {
       this.terrain = terrain;
   }

   public String getPopulation ()
   {
       return population;
   }

   public void setPopulation (String population)
   {
       this.population = population;
   }

   @Override
   public String toString()
   {
       return "ClassPojo [orbital_period = "+orbital_period+", surface_water = "+surface_water+", diameter = "+diameter+", gravity = "+gravity+", name = "+name+", climate = "+climate+", rotation_period = "+rotation_period+", terrain = "+terrain+", population = "+population+"]";
   }
}

Luego de tener el objeto con la información que queremos enviar a través de un request, procedemos a usar métodos de librería RestAssured para poder realizar el POST con el body correspondiente y enviar el request:

PlanetStepDefinitions.java

@When("I create post request to create a Planet")
public void i_create_post_request_to_create_a_planet(){
   response = request.when()
           .body(planet)
           .post("https://swapi.co/api/planets");

Una vez podamos realizar el POST entonces deberíamos validar el response que tenemos, lo podríamos validar de muchas maneras: que la respuesta sea un 200 o 202, que un parámetro de respuesta sea lo que estamos buscando; en este momento eres tu quien debes indicar qué nos dirá que el test está de manera correcta. En el ejemplo, solamente hice una validación que el response diera un código de respuesta 200:

@Then("I get status code 200 from db")
public void i_get_status_code_from_db(){
   Assert.assertEquals(200, response.getStatusCode());
}

Y con esto hemos terminado nuestro test. Recuerda que quien escribe el número de criterios de aceptación para que el test pase eres tú, por esta razón, las validaciones pueden ser infinitas, siempre y cuando estés a gusto con lo que quieras que haga tu test. Para culminar dejo el código completo de la clase que contiene todos los métodos de los steps:

PlanetStepDefinitions.java

package test.stepDefinitions;

import cucumber.api.java.en.And;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import model.CompanyCreate;
import model.StarWarsPlanet;
import org.junit.Assert;

import java.util.Random;

import static io.restassured.RestAssured.given;

public class PlanetStepDefinitions  {
   StarWarsPlanet planet = new StarWarsPlanet();
   private RequestSpecification request;
   private Response response;

   @Given("I have data to create a Planet with {string}, {string}, {string}, {string}, {string}, {string}, {string}, {string} and {string}")
   public void i_have_create_a_planet(String name, String rotation_period, String orbital_period, String diameter, String climate, String gravity, String terrain, String surface_water, String population){
       planet.setName(name);
       planet.setRotation_period(rotation_period);
       planet.setOrbital_period(orbital_period);
       planet.setDiameter(diameter);
       planet.setClimate(climate);
       planet.setGravity(gravity);
       planet.setTerrain(terrain);
       planet.setSurface_water(surface_water);
       planet.setPopulation(population);
   }

   @And("I use planet header")
   public void i_use_planet_header(){
       request = given().header("Content-Type", "application/json");
   }

   @When("I create post request to create a Planet")
   public void i_create_post_request_to_create_a_planet(){
       response = request.when()
               .body(planet)
               .post("https://swapi.co/api/planets");
   }

   @Then("I get status code 200 from db")
   public void i_get_status_code_from_db(){
       Assert.assertEquals(200, response.getStatusCode());
   }

}

Como este proyecto está hecho en maven, para correr el test solo debes hacer un

mvn test

pero, antes, te dejo la clase que ejecutará los tests y a la cual debes configurar dependiendo del nombre de los package que le hayas puesto a tu proyecto

RunTest.java

package test;

import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;
import org.junit.runner.RunWith;

@RunWith(Cucumber.class)
@CucumberOptions(features={"src/test/resources/test"}, dryRun = false,
       glue = {"test.stepDefinitions"})
public class RunTest {
}

El features tendrá la ubicación de los archivos en donde están escrito los tests y el glue tendrá la ubicación de las clases en donde se desarrollaron los step definitions del Gherkins

Y esto es todo, espero que les haya gustado y puedan involucrarse un poco más a la automatización de pruebas para API Rest y luego poder integrarlas dentro de un pipeline y contribuir un poco más al CI. En un próximo capítulo les comentaré de cómo correr este tipo de pruebas y pruebas visuales a través de Pipelines, específicamente en Azure DevOps.

What’s a Rich Text element?

The rich text element allows you to create and format headings, paragraphs, blockquotes, images, and video all in one place instead of having to add and format them individually. Just double-click and easily create content.

Static and dynamic content editing

A rich text element can be used with static or dynamic content. For static content, just drop it into any page and begin editing. For dynamic content, add a rich text field to any collection and then connect a rich text element to that field in the settings panel. Voila!

How to customize formatting for each rich text

Headings, paragraphs, blockquotes, figures, images, and figure captions can all be styled after a class is added to the rich text element using the "When inside of" nested selector system.

Descarga nuestro Clever UI KIT 👇

Acá en Clever Experience trabajamos en un pequeño UI KIT que puede ayudarte en la próxima propuesta rápida, idea o proyecto que debas o quieras desarrollar.
Ingresa tu nombre y correo para descargar.

Gracias. Te será enviado un mail confirmando la inscripción
¡Ups! Algo salió mal al enviar el formulario.