---
title: "Méthodes de test de `test::nginx` : Configuration, envoi de requêtes et gestion des réponses - API7.ai"
description: "Cet article explique comment utiliser test::nginx pour envoyer des requêtes et gérer les réponses dans un cas de test."
url: "https://api7.ai/fr/learning-center/openresty/all-inclusive-testing-methods-test-nginx"
---

# Méthodes de test de \`test::nginx\` : Configuration, envoi de requêtes et gestion des réponses

API7.ai

November 18, 2022

[OpenResty (NGINX + Lua)](https://api7.ai/learning-center/openresty)

Dans l'article précédent, nous avons déjà eu un premier aperçu de `test::nginx` et exécuté l'exemple le plus simple. Cependant, dans un projet open source réel, les cas de test écrits en `test::nginx` sont beaucoup plus complexes et difficiles à maîtriser que le code d'exemple. Sinon, cela ne s'appellerait pas un obstacle.

Dans cet article, je vais vous guider à travers les commandes et méthodes de test fréquemment utilisées dans `test::nginx`, afin que vous puissiez comprendre la plupart des ensembles de cas de test dans le projet OpenResty et avoir la capacité d'écrire des cas de test plus réalistes. Même si vous n'avez pas encore contribué au code d'OpenResty, vous familiariser avec le framework de test d'OpenResty sera une grande source d'inspiration pour concevoir et écrire des cas de test dans votre travail.

Le test `test::nginx` génère essentiellement un `nginx.conf` et démarre un processus NGINX basé sur la configuration de chaque cas de test. Ensuite, il simule une requête client avec le corps et les en-têtes spécifiés. Ensuite, le code Lua dans le cas de test traite la requête et fait une réponse. À ce moment-là, `test::nginx` analyse les informations critiques comme le corps de la réponse, les en-têtes de la réponse et les journaux d'erreur, et les compare à la configuration du test. S'il existe une divergence, le test échoue avec une erreur ; sinon, il réussit.

`test::nginx` fournit beaucoup de primitives DSL (langage spécifique à un domaine). J'ai fait une classification simple selon la configuration de NGINX, l'envoi de requêtes, le traitement des réponses et la vérification des journaux. Ces 20% de fonctionnalités peuvent couvrir 80% des scénarios d'application, donc nous devons les maîtriser fermement. Quant aux autres primitives et utilisations plus avancées, nous les introduirons dans le prochain article.

## Configuration de NGINX

Commençons par regarder la configuration de NGINX. La primitive de `test::nginx` avec le mot-clé "config" est liée à la configuration de NGINX, comme `config`, `stream_config`, `http_config`, etc.

Leurs fonctions sont les mêmes : insérer la configuration NGINX spécifiée dans différents contextes NGINX. Ces configurations peuvent être soit des commandes NGINX, soit du code Lua encapsulé dans `content_by_lua_block`.

Lorsque nous faisons des tests unitaires, `config` est la primitive la plus couramment utilisée, dans laquelle nous chargeons des bibliothèques Lua et appelons des fonctions pour des tests en boîte blanche. Voici un extrait de code de test, qui ne peut pas être exécuté entièrement. Il provient d'un projet open source réel, donc si vous êtes intéressé, vous pouvez cliquer sur le [lien](https://github.com/apache/apisix/blob/master/t/plugin/key-auth.t#L11) pour voir le test complet, ou vous pouvez essayer de l'exécuter localement.

```
=== TEST 1: sanity
--- config
    location /t {
        content_by_lua_block {
            local plugin = require("apisix.plugins.key-auth")
            local ok, err = plugin.check_schema({key = 'test-key'})
            if not ok then
                ngx.say(err)
            end
            ngx.say("done")
        }
    }
```

Le but de ce cas de test est de vérifier si la fonction `check_schema` dans le fichier de code `plugins.key-auth` fonctionne correctement. Il utilise la commande NGINX `content_by_lua_block` dans `location /t` pour exiger le module à tester et pour appeler directement la fonction qui doit être vérifiée.

C'est un moyen courant de test en boîte blanche dans `test::nginx`. Cependant, cette configuration seule ne suffit pas pour compléter le test, donc continuons et voyons comment envoyer une requête client.

## Envoi de Requêtes

Simuler un client envoyant une requête implique pas mal de détails, donc commençons par le plus simple - envoyer une seule requête.

### `request`

Continuons avec le cas de test ci-dessus, si nous voulons que le code de test unitaire soit exécuté, alors nous devons initier une requête HTTP à l'adresse `/t` spécifiée dans la config, comme montré dans le code de test suivant :

```
--- request
GET /t
```

Ce code envoie une requête `GET` à `/t` dans la primitive de requête. Ici, nous ne spécifions pas l'adresse IP, le nom de domaine ou le port d'accès, ni si c'est `HTTP 1.0` ou `HTTP 1.1`. Tous ces détails sont masqués par `test::nginx`, donc nous n'avons pas à nous en soucier. C'est l'un des avantages du DSL - nous avons seulement besoin de nous concentrer sur la logique métier sans être distraits par tous les détails.

De plus, cela offre une certaine flexibilité. Par exemple, le protocole par défaut est `HTTP 1.1`, ou si nous voulons tester `HTTP 1.0`, nous pouvons spécifier séparément :

```
--- request
GET /t  HTTP/1.0
```

En plus de la méthode `GET`, la méthode `POST` doit également être supportée. Dans l'exemple suivant, nous pouvons `POST` la chaîne `hello world` à l'adresse spécifiée.

```
--- request
POST /t  
hello world
```

Encore une fois, `test::nginx` calcule la longueur du corps de la requête pour vous ici, et ajoute les en-têtes de requête `host` et `connection` pour s'assurer que c'est une requête normale automatiquement.

Bien sûr, nous pouvons ajouter des commentaires pour le rendre plus lisible. Ceux qui commencent par `#` seront reconnus comme des commentaires de code.

```
--- request
# post request
POST /t  
hello world
```

La requête supporte également un mode plus complexe et flexible, qui utilise `eval` comme filtre pour intégrer directement du code Perl, puisque `test::nginx` est écrit en Perl. Si le langage DSL actuel ne répond pas à vos besoins, `eval` est l'"arme ultime" pour exécuter directement du code Perl.

Pour l'utilisation de `eval`, regardons quelques exemples simples ici, et nous continuerons avec d'autres plus complexes dans le prochain article.

```
--- request eval
"POST /t
hello\x00\x01\x02
world\x03\x04\xff"
```

Dans le premier exemple, nous utilisons `eval` pour spécifier des caractères non imprimables, ce qui est l'une de ses utilisations. Le contenu entre les guillemets doubles sera traité comme une chaîne Perl puis passé à la `request` comme argument.

Voici un exemple plus intéressant :

```
--- request eval
"POST /t\n" . "a" x 1024
```

Cependant, pour comprendre cet exemple, nous devons savoir quelque chose sur les chaînes en Perl, donc je dois brièvement mentionner deux points ici.

- En Perl, nous utilisons un point pour représenter la concaténation de chaînes. Cela ne ressemble-t-il pas un peu aux deux points de `Lua` ?
- Un `x` minuscule indique le nombre de fois qu'un caractère est répété. Par exemple, le `"a" x 1024` ci-dessus signifie que le caractère "a" est répété 1024 fois.

Donc, le deuxième exemple signifie que la méthode `POST` envoie une requête contenant `1024` caractères `a` à l'adresse `/t`.

### `pipelined_requests`

Après avoir compris comment envoyer une seule requête, regardons comment envoyer plusieurs requêtes. Dans `test::nginx`, nous pouvons utiliser la primitive `pipelined_requests` pour envoyer plusieurs requêtes en séquence dans la même connexion `keep-alive` :

```
--- pipelined_requests eval
["GET /hello", "GET /world", "GET /foo", "GET /bar"]
```

Par exemple, cet exemple accédera à ces quatre API en séquence dans la même connexion. Il y a deux avantages à cela :

- Le premier est qu'une grande quantité de code de test répétitif peut être éliminée, et les quatre cas de test peuvent être compressés en un.
- La deuxième et la plus importante raison est que nous pouvons utiliser les requêtes pipelinées pour détecter si la logique du code aura des exceptions dans le cas de multiples accès.

Vous pourriez vous demander, si j'écris plusieurs cas de test en séquence, alors le code sera également exécuté plusieurs fois dans la phase d'exécution. Cela ne couvre-t-il pas également le deuxième problème ci-dessus ?

Cela revient au mode d'exécution de `test::nginx`, qui fonctionne différemment de ce que vous pourriez penser. Après chaque cas de test, `test::nginx` arrête le processus NGINX actuel, et toutes les données en mémoire disparaissent. Lors de l'exécution du cas de test suivant, `nginx.conf` est régénéré, et un nouveau `Worker` NGINX est démarré. Ce mécanisme garantit que les cas de test ne s'influencent pas mutuellement.

Donc, lorsque nous voulons tester plusieurs requêtes, nous devons utiliser la primitive `pipelined_requests`. Sur cette base, nous pouvons simuler des scénarios de limitation de débit, de limitation de concurrence, et bien d'autres pour tester si votre système fonctionne correctement avec des scénarios plus réalistes et complexes. Nous laisserons cela pour le prochain article également, car cela impliquera plusieurs commandes et primitives.

### `repeat_each`

Nous venons de mentionner le cas de test de plusieurs requêtes, alors comment devrions-nous exécuter le même test plusieurs fois ?

Pour ce problème, `test::nginx` fournit un paramètre global : `repeat_each`, qui est une fonction Perl qui par défaut est `repeat_each(1)`, indiquant que le cas de test ne sera exécuté qu'une seule fois. Donc dans les cas de test précédents, nous ne nous embêtons pas à le définir séparément.

Naturellement, nous pouvons le définir avant la fonction `run_test()`, par exemple, en changeant l'argument à `2`.

```
repeat_each(2);
run_tests();
```

Ensuite, chaque cas de test est exécuté deux fois, et ainsi de suite.

### `more_headers`

Après avoir parlé du corps de la requête, regardons les en-têtes de la requête. Comme nous l'avons mentionné ci-dessus, `test::nginx` envoie la requête avec les en-têtes `host` et `connection` par défaut. Qu'en est-il des autres en-têtes de requête ?

`more_headers` est spécifiquement conçu pour cela.

```
--- more_headers
X-Foo: blah
```

Nous pouvons l'utiliser pour définir divers en-têtes personnalisés. Si nous voulons définir plus d'un en-tête, alors définissons plus d'une ligne :

```
--- more_headers
X-Foo: 3
User-Agent: openresty
```

## Traitement des Réponses

Après avoir envoyé la requête, la partie la plus importante de `test::nginx` est le traitement de la réponse, où nous déterminerons si la réponse répond aux attentes. Ici, nous la divisons en quatre parties et les présentons : le corps de la réponse, l'en-tête de la réponse, le code d'état de la réponse et le journal.

### `response_body`

Le pendant de la primitive de requête est `response_body`, et voici un exemple de leurs deux configurations en utilisation :

```
=== TEST 1: sanity
--- config
    location /t {
        content_by_lua_block {
            ngx.say("hello")
        }
    }
--- request
GET /t
--- response_body
hello
```

Ce cas de test passera si le corps de la réponse est `hello`, et signalera une erreur dans les autres cas. Mais comment testons-nous un corps de retour long ? Ne vous inquiétez pas, `test::nginx` s'en est déjà occupé pour vous. Il supporte la détection du corps de la réponse avec une expression régulière, comme suit :

```
--- response_body_like
^he\w+$
```

Cela vous permet d'être très flexible avec le corps de la réponse. De plus, `test::nginx` supporte également les opérations `unlike` :

```
--- response_body_unlike
^he\w+$
```

À ce stade, si le corps de la réponse est `hello`, le test ne passera pas.

Dans la même veine, après avoir compris la détection d'une seule requête, regardons la détection de plusieurs requêtes. Voici un exemple de son utilisation avec `pipelined_requests` :

```
--- pipelined_requests eval
["GET /hello", "GET /world", "GET /foo", "GET /bar"]
--- response_body eval
["hello", "world", "oo", "bar"]
```

Bien sûr, la chose importante à noter ici est que pour autant de requêtes que vous envoyez, vous devez avoir autant de réponses pour correspondre.

### `response_headers`

Ensuite, parlons de l'en-tête de la réponse. L'en-tête de la réponse est similaire à l'en-tête de la requête en ce que chaque ligne correspond à la clé et à la valeur d'un en-tête.

```
--- response_headers
X-RateLimit-Limit: 2
X-RateLimit-Remaining: 1
```

Comme la détection du corps de la réponse, les en-têtes de réponse supportent également les expressions régulières et les opérations `unlike`, telles que `response_headers_like`, `raw_response_headers_like`, et `raw_response_headers_unlike`.

### `error_code`

Le troisième est le code de réponse. La détection du code de réponse supporte la comparaison directe et supporte également les opérations `like`, comme les deux exemples suivants :

```
--- error_code: 302
```

```
--- error_code_like: ^(?:500)?$
```

Dans le cas de plusieurs requêtes, le `error_code` doit être vérifié plusieurs fois :

```
--- pipelined_requests eval
["GET /hello", "GET /hello", "GET /hello", "GET /hello"]
--- error_code eval
[200, 200, 503, 503]
```

### `error_log`

Le dernier élément de test est le journal d'erreur. Dans la plupart des cas de test, aucun journal d'erreur n'est généré. Nous pouvons utiliser `no_error_log` pour détecter :

```
--- no_error_log
[error]
```

Dans l'exemple ci-dessus, si la chaîne `[error]` apparaît dans le `error.log` de NGINX, le test échouera. C'est une fonctionnalité très courante, et il est recommandé d'ajouter la détection du journal d'erreur à tous vos tests normaux.

```
--- error_log
hello world
```

La configuration ci-dessus détecte la présence de `hello world` dans `error.log`. Bien sûr, vous pouvez utiliser `eval` intégré dans le code Perl pour implémenter la détection par expression régulière, comme suit :

```
--- error_log eval
qr/\[notice\] .*?  \d+ hello world/
```

## Résumé

Aujourd'hui, nous apprenons comment envoyer des requêtes et tester des réponses dans `test::nginx`, contenant le corps de la requête, l'en-tête, le code d'état de la réponse et le journal d'erreur. Nous pouvons implémenter un ensemble complet de cas de test avec la combinaison de ces primitives.

Enfin, voici une question de réflexion : Quels sont les avantages et les inconvénients de `test::nginx`, un DSL abstrait ? N'hésitez pas à laisser des commentaires et à discuter avec moi, et vous êtes également invités à partager cet article pour communiquer et réfléchir ensemble.

Share article link

Share

Related Posts

[![FAQ OpenResty | Structure réseau pour les tests, fonctionnalités liées à SSL, DSL, outil \`ab\`](https://static.api7.ai/2022/12/02/63896c177fde1.png)](https://api7.ai/learning-center/openresty/faq-3)

## [FAQ OpenResty | Structure réseau pour les tests, fonctionnalités liées à SSL, DSL, outil \`ab\`](https://api7.ai/learning-center/openresty/faq-3)

[![Utilisations méconnues de \`test::nginx\`](https://static.api7.ai/2022/11/25/63802a7fb4697.png)](https://api7.ai/learning-center/openresty/little-known-usage-of-test-nginx)

## [Utilisations méconnues de \`test::nginx\`](https://api7.ai/learning-center/openresty/little-known-usage-of-test-nginx)

Table of Contents

- [Configuration de NGINX](#configuration-de-nginx)

- [Envoi de Requêtes](#envoi-de-requ%C3%AAtes)

  - [`request`](#request)
  - [`pipelined_requests`](#pipelined_requests)
  - [`repeat_each`](#repeat_each)
  - [`more_headers`](#more_headers)

- [Traitement des Réponses](#traitement-des-r%C3%A9ponses)

  - [`response_body`](#response_body)
  - [`response_headers`](#response_headers)
  - [`error_code`](#error_code)
  - [`error_log`](#error_log)

- [Résumé](#r%C3%A9sum%C3%A9)
