Karate vs. REST-Assured : Tests d’API Automatisés avec Java

Les tests d’API constituent une étape critique du processus QA qui consiste à analyser les interfaces de programmation d’application (API) pour évaluer leur fonctionnalité, leur sécurité, leurs performances et leur fiabilité.

En particulier, pour les applications complexes, l’API peut comprendre des dizaines d’endpoints et de déclencheurs qui s’influencent mutuellement. Pour éviter les problèmes de régression en production, il est très avantageux de disposer d’un ensemble de tests automatisés de bout en bout. Cependant, avant de mettre en œuvre ces tests, les ingénieurs QA en automatisation (AQA) et la direction doivent déterminer le framework sur lequel ces tests seront basés.

Dans cet article, nous allons explorer et comparer les deux frameworks de tests d’API les plus populaires du marché : Karate et REST-Assured. Les deux frameworks sont basés sur Java. La comparaison fournie vous aidera à décider quel framework convient le mieux à vos besoins spécifiques. C’est parti !

Présentation de Karate

Karate est un framework open-source relativement récent qui permet aux testeurs, même sans expérience en programmation, de réaliser des tests d’API, de services web et de microservices. Vous pourriez penser qu’il vient du Japon, n’est-ce pas ? En réalité, ce framework a été développé par Peter Thomas en 2017 et a depuis été adopté par des entreprises du monde entier.

Examinons les détails de l’outil Karate. En termes d’installation, Karate est disponible en tant que fichier JAR unique et exécutable. Il peut également être ajouté comme dépendance Maven au fichier pom.xml. Si vous préférez ne pas l’intégrer à un framework de test, vous pouvez opter pour la version core :

<!-- https://mvnrepository.com/artifact/com.intuit.karate/karate-core -->
<dependency>
<groupId>com.intuit.karate</groupId>
 	<artifactId>karate-core</artifactId>
    	<version>1.2.0</version>
</dependency>

Sinon, il existe des versions compatibles avec les frameworks de test TestNG et JUnit.

<!-- https://mvnrepository.com/artifact/com.intuit.karate/karate-junit5 -->
<dependency>
    	<groupId>com.intuit.karate</groupId>
    	<artifactId>karate-junit5</artifactId>
    	<version>1.2.0 </version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.intuit.karate/karate-testng -->
<dependency>
    	<groupId>com.intuit.karate</groupId>
    	<artifactId>karate-testng</artifactId>
    	<version>0.8.0.1</version>
</dependency>

Karate utilise son propre langage de script de test appelé « Karate-Script ». Si vous avez de l’expérience avec des frameworks BDD (behavior-driven development) comme Cucumber, vous constaterez que la structure du code de Karate lui ressemble étroitement :

Feature: sample karate test script
 for help, see https://github.com/karatelabs/karate/wiki/IDE-support

 Background:
   * url 'https://jsonplaceholder.typicode.com/'

   Scenario: get all users and then get the first user by id
     Given path 'users'
     When method get
     Then status 200

     * def first = response[0]

     Given path 'users', first.id
     When method get
     Then status 200
     And assert response.name == "Leanne Graham"
     And assert response.email == "[email protected]"

En effet, Karate partage de nombreuses similitudes avec Cucumber :

  1. des mots-clés comme « Feature », « Scenario », « Given », « When », « Then », « And »
  2. le stockage des tests dans des fichiers .feature

Cependant, le principal facteur différenciateur de Karate est que « toutes les définitions d’étapes sont déjà écrites pour nous ». C’est particulièrement avantageux pour les tests d’API. Contrairement aux tests UI, les tests d’API suivent généralement un schéma uniforme :

  1. Background
  2. Fourniture des paramètres de requête
  3. Validation du statut de la réponse

Il est donc très judicieux d’utiliser des définitions d’étapes prépréparées plutôt que de créer les siennes. Voyons maintenant comment Karate gère chacune de ces trois étapes.

Background

Le Background inclut généralement des propriétés communes partagées entre tous les scénarios du fichier .feature. Certaines de ces propriétés sont déjà prédéfinies, comme l’URL :

Background:
 * url 'https://jsonplaceholder.typicode.com'

Et vous pouvez définir directement des variables dans le fichier feature en utilisant le mot-clé « def » :

* def user =
 """
 {
   "name": "Test User",
   "username": "testuser",
   "email": "[email protected]",
   "address": {
     "street": "Has No Name",
     "suite": "Apt. 123",
     "city": "Electri",
     "zipcode": "54321-6789"
   }
 }
 """

Fourniture des paramètres de requête

Tout d’abord, nous devons fournir un chemin qui peut également inclure des paramètres de requête séparés par des virgules.

Given path 'users', 1234

Ensuite, nous pouvons inclure le payload de la requête si nécessaire, qui prend en charge les formats JSON et XML.

JSON :

And request 
{
   "name":"Vidhya",
   "age":29,"locale":"en",
   "twitter":"VidhyaJava",
   "email":"[email protected]"
}

XML :

 * def payload =
 """
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
"""
 And request payload

Enfin, nous spécifions la méthode HTTP pour la requête, qui peut être l’une des suivantes : get, post, delete, patch ou put.

When method get

Validation de la réponse

Tout d’abord, nous validons le code de réponse pour les cas de test positifs (200, 201) et négatifs (4xx).

Then status 201
Then status 404

Ensuite, nous procédons à la validation du schéma de réponse, où nous pouvons vérifier le payload de réponse aux formats JSON et XML. Dans le cas de JSON :

{
  "id": 1,
  "name": "Leanne Graham",
  "username": "Bret",
  "email": "[email protected]",
  "address": {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  },
  "phone": "1-770-736-8031 x56442",
  "website": "hildegard.org",
  "company": {
    "name": "Romaguera-Crona",
    "catchPhrase": "Multi-layered client-server neural-net",
    "bs": "harness real-time e-markets"
  }
}

Si nous souhaitons valider des propriétés spécifiques, les assertions peuvent inclure :

And assert response.name == "Leanne Graham"
And assert response.email == "[email protected]"

Cependant, il est important de noter que Karate fournit un mécanisme intégré pour valider l’intégralité de la réponse JSON, contrairement à REST-Assured :

And match response == {
	"id":1,"name":"LeanneGraham","username":"Bret",
        "email":"[email protected]",
        "address":{"street":"Kulas Light","suite":"Apt. 556",
        "city":"Gwenborough","zipcode":"92998-3874",
        "geo":{"lat":"-37.3159","lng":"81.1496"}},
        "phone":"1-770-736-8031x56442",
        "website":"hildegard.org",
        "company":{
	    "name":"Romaguera-Crona",
            "catchPhrase":"Multi-layered client-server neural-net",
            "bs":"harness real-time e-markets"
   }
}

Karate inclut un outil de reporting intégré qui génère des rapports dans le répertoire targetkarate-reports. Le fichier de rapport principal est karate-summary.html :

Karate vs. REST-Assured : Tests d’API Automatisés avec Java

Présentation de REST-Assured

REST-Assured est une bibliothèque basée sur Java créée par JayWay Company pour simplifier les tests et la validation des services web RESTful. Elle sert de catalyseur efficace pour automatiser le processus de test des API REST.

REST-Assured peut être facilement installé en ajoutant la dépendance Maven :

<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>5.1.1</version>
<scope>test</scope>
</dependency>

Un framework de test comme JUnit ou TestNG est requis en tant que runner de tests. L’exemple de code ci-dessous illustre l’utilisation de JUnit. Examinons maintenant un cas de test basé sur REST-Assured :

public class UsersTest {

   @Before
   public void setup() {
       RestAssured.baseURI = "https://jsonplaceholder.typicode.com/";
   }

   @Test
   public void getAllUsersAndThenGetFirstUserById() {
       Response responseAllUsers =
               given()
                       .when()
                       .get("/users")
                       .then()
                       .statusCode(200)
                       .extract()
                       .response();

       int firstUserId = responseAllUsers.jsonPath().getInt("id[0]");

       Response responseFirstUser =
               given()
                       .pathParam("id", firstUserId)
                       .when()
                       .get("/users/{id}")
                       .then()
                       .statusCode(200)
                       .extract()
                       .response();
       Assert.assertEquals(responseFirstUser.jsonPath().getString("name"), 
      "Leanne Graham");
       Assert.assertEquals(responseFirstUser.jsonPath().getString("email"), 
       "[email protected]");
   }
}

Nous constatons que les tests basés sur REST-Assured sont écrits dans un format BDD, suivant la structure Given-When-Then. Cependant, contrairement à Karate, ces tests sont intégrés dans le code Java. En d’autres termes, pour visualiser les cas de test implémentés, il faut plonger dans les tests API et examiner le code. Par défaut, REST-Assured n’utilise pas de fichiers .feature, bien qu’il puisse être intégré avec des outils comme Cucumber ou d’autres frameworks BDD.

REST-Assured gère les étapes du schéma de tests API de la manière suivante (en utilisant l’exemple du framework de test JUnit) :

Background

Nous pouvons simplement utiliser l’annotation @Before :

@Before
public void setup() {
   RestAssured.baseURI = "https://jsonplaceholder.typicode.com";
}

Fourniture des paramètres de requête

Cette étape est divisée en 2 parties par les mots-clés given() et when().

  1. Avec given(), nous pouvons fournir le content-type et le corps de la requête, si nécessaire. Il est possible de passer des payloads JSON ou XML à un objet String :
public void createUser() {
   String body = "{"name": "Test User"," +
           ""username": "testuser", " +
           ""email": "[email protected]"," +
           ""address": " +
           "{ "street": "Has No Name"," +
           ""suite": "Apt. 123"," +
           ""city": "Electri"," +
           ""zipcode": "54321-6789"}}";
   Response response =
           given()
                   .contentType(ContentType.JSON)
                   .body(body)

Une approche plus précise consiste à utiliser l’Object Mapping lors de l’utilisation de REST-Assured. Définissez le schéma du payload de requête dans la classe appropriée :

public static class User {
   private String name;
   private String username;

   public User(String name, String username) {
       this.name = name;
       this.username = username;
   }

   public String getName() {
       return name;
   }

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

   public String getUsername() {
       return username;
   }

   public void setUsername(String job) {
       this.username = username;
   }
}

Ensuite, vous pouvez simplement passer une nouvelle instance de la classe dans le payload de la requête :

@Test
public void createUser() {
   User user = new User("Test User", "testuser");
   Response response =
           given()
                   .contentType(ContentType.JSON)
                   .body(user)

Alternativement, nous pouvons importer le payload directement depuis un fichier :

@Test
public void createUserXML() {
   File xmlDataInFile = new File("src/test/resources/user.xml");
   Response response =
           given()
                   .contentType(ContentType.XML)
                   .body(xmlDataInFile)
  1. Avec when(), nous devons spécifier le chemin et la méthode :
.when()
.get("/users")

Le chemin dans REST-Assured accepte également des variables :

int firstUserId = 123;
*********************
.pathParam("id", firstUserId)
.when()
.get("/users/{id}")

Validation de la réponse

La validation de la réponse se fait avec le mot-clé then(). Tout d’abord, nous validons généralement le code de statut de la réponse :

.then()
.statusCode(200)

Ensuite, nous pouvons extraire l’objet de réponse :

.extract()
.response();

et valider certaines de ses propriétés :

Assert.assertEquals(responseFirstUser.jsonPath().getString("name"), "Leanne Graham");
Assert.assertEquals(responseFirstUser.jsonPath().getString("email"), "[email protected]");

En termes de reporting, REST-Assured ne fournit pas d’outil de reporting intégré. Il est donc nécessaire d’utiliser une bibliothèque de reporting externe appropriée comme Allure Report.

Karate vs REST-Assured

Il est maintenant temps de comparer ces deux outils et de décider lequel choisir pour nos besoins spécifiques. Comparons leurs performances pour le scénario de test simple mentionné ci-dessus :

Karate vs. REST-Assured : Tests d’API Automatisés avec Java
Karate vs. REST-Assured : Tests d’API Automatisés avec Java

Comme nous pouvons le voir, Karate prend beaucoup moins de temps à s’exécuter que REST-Assured dans le cas d’un scénario unique. Cependant, en considérant plusieurs scénarios (c’est-à-dire l’ensemble du périmètre de test), on peut s’attendre à ce que la différence ne soit pas aussi significative.

La comparaison des deux frameworks sur la base d’autres critères principaux est présentée dans le tableau ci-dessous :

Karate
REST-Assured

BDD Syntax

Karate

Yes

REST-Assured

Yes

Test-Scripting Language

Karate

Karate-Script (Gherkin)

REST-Assured

Java

Validate whole JSON response

Karate

Yes

REST-Assured

No (extra Java library required)

Test runner

Karate

Optional (JUnit, TestNG)

REST-Assured

Required (JUnit, TestNG)

Retry ability

Karate

Yes

REST-Assured

No (extra Java library required)

Reporting tool

Karate

Built-in

REST-Assured

External tool required (Allure Report, etc.)

Read file

Karate

Yes

REST-Assured

No (extra Java library required)

Performance testing

Karate

Yes (possible to re-use Karate tests Gatling tests)

REST-Assured

No

Karate et REST-Assured ont tous deux leurs points forts et leurs faiblesses. En général, Karate dispose d’un plus grand nombre d’outils intégrés par rapport à REST-Assured. Cela signifie que vous pouvez commencer à écrire des tests sans passer de temps supplémentaire à configurer des bibliothèques externes. De plus, Karate utilise un langage de script de test simple et direct, ce qui le rend adapté aux équipes avec une expérience Java limitée. Si vous êtes satisfait des rapports standard de Karate et ne souhaitez pas investir du temps dans la configuration de rapports personnalisés, c’est peut-être un meilleur choix.

D’un autre côté, REST-Assured exige une solide expérience Java de la part de l’équipe QA Automation et l’utilisation de bibliothèques supplémentaires. Cependant, dans le cas d’API complexes et hautement personnalisées, REST-Assured pourrait être un meilleur choix. Écrire des tests directement en Java peut être plus facile dans de tels cas.

REST-Assured permet également l’utilisation de bibliothèques comme les matchers Hamcrest, offrant plus d’options pour des validations supplémentaires dans des scénarios spécifiques. Si vous préférez utiliser le BDD et que les définitions d’étapes prépréparées de Karate ne répondent pas à vos exigences, REST-Assured peut être utilisé conjointement avec un outil BDD externe comme Cucumber.

Réflexions Finales

En conclusion, il convient de noter que les outils Karate et REST-Assured sont tous deux activement développés et bien documentés. En définitive, le choix entre Karate et REST-Assured doit être basé sur des facteurs tels que la complexité de votre API, le niveau d’expertise Java au sein de votre équipe, le besoin d’options de validation ou de personnalisation spécifiques, et votre approche de test préférée (par exemple, BDD).

Avec les informations fournies ci-dessus, vous pouvez prendre une décision éclairée sur l’outil qui convient le mieux à vos besoins spécifiques. Bonne chance !

See how we helped Union54
future-proof Zambia #1 card-issuing
API  with test automation

Veuillez saisir votre adresse courriel professionnelle n'est pas un courriel professionnel