claria.tools.integrated-test-runner 1.1.1-build.20260814.1

Build Information

Provenance Azure DevOps Pipeline
Owner kntera (Yan Boivin)
Date de compilation 2026-08-14 15:48:39 UTC
Version 1.1.1.20260814.1
SHA256 5708f0e2209552659a98441e82960ffb802a5f1a

Integrated Test Runner

Outil de tests d'intégration en ligne de commande pour valider des API REST.
Il exécute des fichiers .http (format REST Client / JetBrains HTTP) et permet d'écrire des assertions et des scripts C# directement dans les fichiers de test.


Contexte

Les tests d'intégration classiques nécessitent souvent un projet de test séparé avec beaucoup de boilerplate. Cet outil adopte une approche différente : les scénarios sont décrits dans des fichiers .http lisibles, qui combinent les appels HTTP et les scripts de validation C# dans un seul fichier.

Cas d'usage typique :

  • Valider un flux complet sur une API (créer une ressource → la récupérer → la modifier → la supprimer)
  • Partager des variables entre les requêtes (ex. : capturer l'id d'une ressource créée pour l'utiliser dans les requêtes suivantes)
  • Lancer l'application à tester automatiquement avant les tests, y compris une base SQL LocalDB

Utilisation

IntegratedTestRunner.Console.exe [--debug] [--config <config.json>] <pattern|fichier.http>
Argument Description
<pattern> Chemin vers un fichier .http ou un pattern glob (ex. tests/**/*.http)
--config <path> Fichier de configuration JSON (optionnel)
--debug Affiche les détails des requêtes et réponses

Exemples :

# Exécuter un seul fichier
IntegratedTestRunner.Console.exe tests/transactions.http

# Exécuter tous les fichiers .http d'un dossier
IntegratedTestRunner.Console.exe "tests/**/*.http"

# Avec configuration (démarrage de l'application)
IntegratedTestRunner.Console.exe --config testconfig.json "tests/**/*.http"

# Mode debug
IntegratedTestRunner.Console.exe --debug tests/transactions.http

Sortie console

L'exécutable affiche les résultats au fur et à mesure de l'exécution, groupés par dossier, puis imprime un résumé global à la fin.

Format ligne par ligne

./tests/api/
    create-transaction.http ... OK (142ms)
    get-transaction.http ... OK (98ms)
    delete-transaction.http ... [AssertionException] Expected 204 but got 200 (75ms)
    <réponse HTTP brute affichée ici en cas d'échec>

./tests/auth/
    login.http ... OK (203ms)
    refresh.http ... INCONCLUSIVE: Token non encore supporté en environnement de test. (11ms)
Résultat Format affiché
Succès ... OK (Xms)
Échec ... [NomException] Message d'erreur (Xms) suivi du corps de la réponse HTTP
Non concluant ... INCONCLUSIVE: Message (Xms)

Résumé final

À la fin de l'exécution, un résumé global est affiché :

─────────────────────────────────────────
  Tests : 5   OK : 3 (60%)   FAILED : 1 (20%)   INCONCLUSIVE : 1 (20%)
  Total : 1.23s   Average : 246ms
─────────────────────────────────────────
Champ Description
Tests Nombre total de fichiers .http exécutés
OK Tests terminés sans erreur
FAILED Tests ayant levé une exception (assertion ou HTTP)
INCONCLUSIVE Tests marqués comme non concluants via Assert.Inconclusive()
Total Durée totale de l'exécution
Average Durée moyenne par test

Structure d'un fichier .http

Un fichier .http contient une ou plusieurs requêtes séparées par ---.
Chaque requête peut être suivie d'un bloc script C# (délimité par ```).

### Nom de la requête (optionnel)
MÉTHODE URL
En-tête: Valeur

{ corps JSON }

``` csharp
// script de validation post-requête

Requête suivante

...


### Formats de corps supportés

| `Content-Type` | Comportement |
|---|---|
| `application/json`, `text/plain`, etc. | Corps envoyé tel quel en UTF-8 |
| `multipart/form-data; boundary=...` | Corps parsé et envoyé comme `MultipartFormDataContent` — chaque partie délimitée par le `boundary` est transmise individuellement |

---

### Substitutions de variables

Dans les URLs, en-têtes et corps, deux types de substitutions sont disponibles :

| Syntaxe | Description |
|---|---|
| `{{env:clé}}` | Valeur stockée dans l'environnement partagé par un script précédent |
| `{{code:expression}}` | Expression C# évaluée à l'exécution |

```http
### Récupérer la transaction créée
GET http://localhost:5086/api/transactions/{{env:transaction.publicId}}

---

### Créer avec une date dynamique
POST http://localhost:5086/api/reservations
Content-Type: application/json

{
  "date": "{{code:DateTime.UtcNow.AddDays(7).ToString("yyyy-MM-dd")}}"
}

Exemples de fichiers de test

Exemple 1 — Créer et chaîner des ressources

### Créer une transaction
POST http://localhost:5086/api/transactions
Content-Type: application/json

{
  "advisorLicenseNumber": "LIC-00123",
  "clientFirstName": "Marie",
  "clientLastName": "Tremblay",
  "plannedTransactionDate": "2026-06-15T00:00:00Z",
  "businessUnit": "Mortgage",
  "category": "Mortgage"
}

``` csharp
EnsureHttpSucceeded();
var publicId = jq.getString("$.publicId");
env.Add("transaction.publicId", publicId);

Récupérer la transaction créée

GET http://localhost:5086/api/transactions/{{env:transaction.publicId}}

EnsureHttpSucceeded();
Assert.AreEqual("Mortgage", jq.getString("$.category"));

Supprimer la transaction

DELETE http://localhost:5086/api/transactions/{{env:transaction.publicId}}

EnsureHttpStatusEqual(204);

---

### Exemple 2 — Utiliser des données générées aléatoirement

```http
### Créer un client avec des données fictives
POST http://localhost:5086/api/clients
Content-Type: application/json

{
  "firstName": "{{code:new Bogus.Faker("fr_CA").Name.FirstName()}}",
  "lastName":  "{{code:new Bogus.Faker("fr_CA").Name.LastName()}}",
  "email":     "{{code:new Bogus.Faker().Internet.Email()}}"
}

``` csharp
EnsureHttpSucceeded();
var clientId = jq.getString("$.id");
env.Add("clientId", clientId);

> **Note :** À l'intérieur des scripts, `Faker` est directement disponible (voir section [Fonctions disponibles dans les scripts](#fonctions-disponibles-dans-les-scripts)).

---

### Exemple 3 — Envoyer un formulaire multipart

```http
### Téléverser un fichier
POST http://localhost:5086/api/uploads
Content-Type: multipart/form-data; boundary=----boundary

------boundary
Content-Disposition: form-data; name="description"

Rapport mensuel
------boundary
Content-Disposition: form-data; name="file"; filename="rapport.txt"
Content-Type: text/plain

Contenu du fichier ici
------boundary--

``` csharp
EnsureHttpSucceeded();
var fileId = jq.getString("$.fileId");
env.Add("upload.fileId", fileId);

> **Note :** La valeur du `boundary` dans le `Content-Type` doit correspondre exactement aux marqueurs `--boundary` dans le corps.  
> Chaque partie peut avoir ses propres en-têtes (`Content-Type`, `Content-Disposition`). L'en-tête `Content-Type` est optionnel par partie ; s'il est absent, la partie est traitée comme texte UTF-8.

---

### Exemple 4 — Tester une réponse d'erreur attendue

```http
### Tenter une création invalide
POST http://localhost:5086/api/transactions
Content-Type: application/json

{
  "advisorLicenseNumber": ""
}

``` csharp
EnsureHttpStatusEqual(400);
var message = jq.getString("$.errors.AdvisorLicenseNumber[0]");
Assert.IsNotNull(message);

---

## Configuration

Un fichier de configuration JSON permet de démarrer automatiquement l'application à tester avant les tests.

### Structure

```json
{
  "application": {
    "path": "dotnet",
    "arguments": "run --project ../MyApi",
    "workingDirectory": "./src",
    "readyWhenOutputContains": "Application started",
    "startupTimeoutSeconds": 60,
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Testing",
      "FeatureFlags__NewCheckout": "true"
    },
    "sqlLocalDb": {
      "instanceName": "MSSQLLocalDB",
      "connectionStringEnvironmentVariable": "ConnectionStrings__DefaultConnection"
    }
  }
}

Propriétés

Propriété Type Défaut Description
application.path string — Exécutable à lancer (ex. dotnet)
application.arguments string "" Arguments de la commande
application.workingDirectory string Dossier courant Répertoire de travail du processus
application.readyWhenOutputContains string null Attend que cette chaîne apparaisse dans stdout/stderr avant de démarrer les tests. Si absent, attend 1 seconde.
application.startupTimeoutSeconds int 30 Délai maximal d'attente du signal de démarrage
application.environmentVariables object {} Variables d'environnement supplémentaires à injecter dans le processus lancé (paires clé/valeur)
application.sqlLocalDb.instanceName string "MSSQLLocalDB" Nom de l'instance SQL LocalDB à démarrer
application.sqlLocalDb.connectionStringEnvironmentVariable string — Variable d'environnement dans laquelle injecter la chaîne de connexion

Exemple sans base de données

{
  "application": {
    "path": "dotnet",
    "arguments": "run --project ../MyApi",
    "readyWhenOutputContains": "Now listening on:"
  }
}

Exemple avec variables d'environnement

{
  "application": {
    "path": "dotnet",
    "arguments": "run --project ../MyApi",
    "readyWhenOutputContains": "Now listening on:",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Testing",
      "FeatureFlags__NewCheckout": "true"
    }
  }
}

Les variables définies dans environmentVariables sont injectées en premier. La chaîne de connexion SQL LocalDB (si configurée) est injectée ensuite et peut écraser une variable du même nom.

Exemple avec SQL LocalDB

{
  "application": {
    "path": "dotnet",
    "arguments": "run --project ../MyApi",
    "readyWhenOutputContains": "Application started",
    "startupTimeoutSeconds": 90,
    "sqlLocalDb": {
      "instanceName": "MSSQLLocalDB",
      "connectionStringEnvironmentVariable": "ConnectionStrings__DefaultConnection"
    }
  }
}

Fonctions disponibles dans les scripts

Chaque bloc script C# a accès aux objets globaux suivants.


jq — Requêtes JSONPath sur la réponse

Permet d'extraire des valeurs de la réponse JSON de la requête courante.

Méthode Retour Description
jq.getString("$.champ") string? Valeur texte. null si la valeur JSON est null. Lève KeyNotFoundException si le chemin n'existe pas.
jq.getStringArray("$.champ") string[] Tableau de valeurs texte. Fonctionne aussi avec les wildcards ($.items[*].id). Retourne [] si le chemin n'est pas trouvé.
jq.getInt("$.champ") int? Valeur entière, null si absent.
jq.getBool("$.champ") bool? Valeur booléenne, null si absent.
jq.getBoolean("$.champ") bool? Identique à getBool, lève KeyNotFoundException si le chemin n'existe pas.
jq.getDateTime("$.champ") DateTime? Valeur date/heure (ISO 8601), null si absent.
var id     = jq.getString("$.publicId");
var tags   = jq.getStringArray("$.tags");
var amount = jq.getInt("$.amount");
var active = jq.getBool("$.isActive");
var date   = jq.getDateTime("$.createdAt");

env — Variables partagées entre les requêtes

Dictionnaire Dictionary<string, string> persisté entre toutes les requêtes du fichier.
Permet de passer des valeurs d'une requête à la suivante.

// Stocker une valeur
env.Add("transaction.publicId", jq.getString("$.publicId"));
env["token"] = jq.getString("$.accessToken");

// Lire une valeur (dans un script suivant)
var id = env["transaction.publicId"];

Pour utiliser une valeur dans l'URL ou le corps de la requête suivante :

GET http://localhost:5086/api/items/{{env:transaction.publicId}}

Assert — Assertions de test

Lève une AssertionException (et fait échouer le test) si la condition n'est pas satisfaite.

Méthode Description
Assert.AreEqual(expected, actual) Vérifie l'égalité
Assert.AreNotEqual(notExpected, actual) Vérifie l'inégalité
Assert.IsTrue(condition) Vérifie que la condition est vraie
Assert.IsFalse(condition) Vérifie que la condition est fausse
Assert.IsNull(value) Vérifie que la valeur est nulle
Assert.IsNotNull(value) Vérifie que la valeur n'est pas nulle
Assert.Contains(expected, actual) Vérifie qu'une chaîne en contient une autre
Assert.Fail() Fait échouer le test immédiatement
Assert.Fail(message) Fait échouer le test immédiatement avec un message explicatif
Assert.Inconclusive() Marque le test comme non concluant (ni succès ni échec)
Assert.Inconclusive(message) Marque le test comme non concluant avec un message explicatif

Toutes les méthodes acceptent un paramètre message optionnel pour personnaliser le message d'erreur.

Assert.AreEqual("Mortgage", jq.getString("$.category"));
Assert.IsNotNull(jq.getString("$.publicId"));
Assert.IsTrue(jq.getInt("$.amount") > 0, "Le montant doit être positif");
Assert.Contains("Tremblay", jq.getString("$.clientLastName"));

Assert.Inconclusive — Test non concluant

Lève une InconclusiveException qui interrompt le test sans le comptabiliser comme un échec.
Utile pour signaler qu'un scénario ne peut pas être validé dans les conditions actuelles (fonctionnalité non encore disponible, données manquantes, prérequis non rempli, etc.).

La sortie affiche INCONCLUSIVE au lieu d'un message d'erreur.

// Sans message
Assert.Inconclusive();

// Avec message explicatif
Assert.Inconclusive("Fonctionnalité non encore déployée en environnement de test.");

// Exemple conditionnel
var status = jq.getString("$.status");
if (status == "pending")
    Assert.Inconclusive($"La ressource est encore en attente (status={status}), test ignoré.");

Sortie correspondante :

./tests/
    montest.http ... INCONCLUSIVE: Fonctionnalité non encore déployée en environnement de test.

EnsureHttpSucceeded() — Validation du statut HTTP 2xx

Vérifie que la réponse HTTP a un code de statut entre 200 et 299 (inclus).
Équivalent d'un Assert sur le code HTTP.

EnsureHttpSucceeded(); // Lève une exception si le statut n'est pas 2xx

EnsureHttpStatusEqual(code) — Validation d'un statut HTTP précis

Vérifie que le code de statut HTTP correspond exactement à la valeur attendue.

EnsureHttpStatusEqual(201); // Created
EnsureHttpStatusEqual(204); // No Content
EnsureHttpStatusEqual(400); // Bad Request (pour tester les erreurs attendues)

Faker — Génération de données fictives

Basé sur la librairie Bogus. Génère des données aléatoires réalistes pour les tests.

Propriété Description
Faker.Name Noms de personnes (FirstName(), LastName(), FullName())
Faker.Internet Données internet (Email(), UserName(), Url(), Ip())
Faker.Phone Numéros de téléphone (PhoneNumber())
Faker.Address Adresses (City(), Country(), ZipCode(), StreetAddress())
Faker.Date Dates (Past(), Future(), Between(from, to))
Faker.Lorem Texte (Word(), Sentence(), Paragraph())
Faker.Finance Finances (Amount(), Currency(), Iban())
Faker.Company Entreprises (CompanyName(), Bs(), CatchPhrase())
Faker.Random Aléatoire générique (Number(), Guid(), Bool())
var email     = Faker.Internet.Email();
var firstName = Faker.Name.FirstName();
var amount    = Faker.Finance.Amount(100, 10000);
var phone     = Faker.Phone.PhoneNumber("###-###-####");

env.Add("clientEmail", email);
env.Add("clientName", firstName);

Le Faker dans les scripts utilise la locale "en" par défaut.
Pour générer des données localisées, utilisez {{code:new Bogus.Faker("fr_CA").Name.FirstName()}} dans les substitutions de l'URL ou du corps.

No packages depend on claria.tools.integrated-test-runner.

Build 1.1.1.20260814.1 du 2026-08-14 15:48:39 UTC - pipeline skill tools - integrated-test-runner run 2027 - commit 5708f0e2209552659a98441e82960ffb802a5f1a

This package has no dependencies.

Version Downloads Last updated
1.1.1-build.20260814.1 11 08/14/2026