Qu’est-ce que le test BDD ? Cadre de développement piloté par le comportement
⚡ Résumé intelligent
Les tests BDD décrivent le comportement d'une application en langage clair « Étant donné-Quand-Alors », permettant ainsi aux analystes, développeurs et testeurs de partager une spécification commune. Ce guide applique cette approche aux tests d'API REST avec Behave. Python cadre, couvrant la configuration, les fichiers de fonctionnalités, les implémentations d'étapes, l'exécution et la génération de rapports.

Qu'est-ce que les tests BDD (Behaviour Driven Development) ?
Tests BDD (Développement piloté par le comportement) Le BDD est une technique de développement logiciel agile et une extension du TDD (Test Driven Development). En BDD, les cas de test sont rédigés dans un langage naturel compréhensible même par les non-programmeurs.
Comment fonctionnent les tests BDD ?
Imaginez que vous êtes chargé de créer un module de transfert de fonds dans une application de banque en ligne.
Il existe plusieurs façons de le tester :
- Le transfert de fonds devrait avoir lieu si le solde du compte source est suffisant.
- Le transfert de fonds doit avoir lieu si les détails de la climatisation de destination sont corrects.
- Le transfert de fonds devrait avoir lieu si le mot de passe de transaction / le code RSA / l'authentification de sécurité saisis par l'utilisateur sont corrects.
- Le transfert de fonds doit être effectué même s'il s'agit d'un jour férié.
- Le transfert de fonds doit avoir lieu à une date ultérieure fixée par le titulaire du compte.
Le Scénario de test La procédure devient plus élaborée et complexe lorsque l'on considère des fonctionnalités supplémentaires telles que le transfert d'un montant X pendant une période de Y jours ou mois, ou l'arrêt de ce transfert.ping un virement programmé lorsque le total atteint Z, et ainsi de suite.
La tendance générale des développeurs est de développer les fonctionnalités et d'écrire le code de test ultérieurement. Comme le montre l'exemple ci-dessus, Cas de test Le développement est complexe ici, donc le développeur va le reporter. Tests jusqu'à la mise en production, moment auquel des tests rapides mais inefficaces sont effectués.
Pour pallier ce problème, le développement piloté par le comportement (BDD) a été conçu. Il simplifie l'ensemble du processus de test pour le développeur.
En BDD, tout ce que vous écrivez doit être placé dans Étant donné-quand-alors étapes. Prenons le même exemple que ci-dessus dans le cadre du BDD :
Given that a fund transfer module in net banking application has been developed And I am accessing it with proper authentication When I shall transfer with enough balance in my source account Or I shall transfer on a Bank Holiday Or I shall transfer on a future date And destination a/c details are correct And transaction password / rsa code / security authentication for the transaction is correct And press or click send button Then amount must be transferred And the event will be logged in log file
Ce formulaire est facile à rédiger, à lire et à comprendre. Il couvre tous les cas de test possibles pour le module de transfert de fonds et peut être rapidement modifié pour en intégrer davantage. Il constitue également une documentation évolutive pour le module. Le BDD étant issu du TDD, il est important de bien comprendre la différence entre les deux avant de passer aux outils.
BDD vs TDD : Principales différences
Ces deux pratiques privilégient les tests préalables et raccourcissent les cycles de retour d'information. Elles diffèrent par l'auteur des tests, le langage utilisé et la couche applicative qu'ils décrivent.
| Aspect | Développement piloté par les tests (TDD) | BDD (développement axé sur le comportement) |
|---|---|---|
| Focus | Fonctionnement interne d'une unité de code | Comportement du système du point de vue de l'utilisateur |
| Langue | assertions de langage de programmation | Scénarios de langage naturel Étant donné-Quand-Alors |
| Auteur principal | Développeur | Analyste d'affaires, responsable produit, testeur et développeur ensemble |
| Readable par des non-programmeurs | Non | Oui |
| Portée typique | Niveau de l'unité | Niveau de fonctionnalités, d'API et d'acceptation |
| Outils communs | JUnit, pytest, NUnit | Se comporter, CucumberSpecFlow, JBehave |
Les deux sont complémentaires plutôt que concurrents. Les équipes conservent généralement TDD Pour la conception au niveau unitaire, ajoutez des scénarios BDD afin de décrire le comportement validé par les parties prenantes. Les points de terminaison REST se situant précisément à ce niveau d'acceptation, ils sont parfaitement adaptés à l'approche BDD.
Qu'est-ce que les tests d'API REST ?
Avec la popularité croissante de REST pour la création d'API, l'automatisation des cas de test d'API REST, au même titre que celle des cas de test d'interface utilisateur, est devenue tout aussi importante. Test d'API implique de tester les actions CRUD (Créer-Lire-Mettre à jour-Supprimer) avec les méthodes POST, GET, PUT et DELETE respectivement.
Qu'est-ce que le comportement ?
Le comportement est l'un des plus populaires Python Frameworks de tests BDD. Voici comment fonctionne Behave :
- Les fiches de fonctionnalités sont rédigées par votre analyste métier, votre commanditaire ou toute personne responsable des scénarios de comportement. Une fiche de fonctionnalités utilise un format en langage naturel pour décrire une fonctionnalité, ou une partie de fonctionnalité, avec des exemples représentatifs des résultats attendus.
- Ces étapes de scénario sont mappées sur des implémentations d'étapes écrites en Python.
- En option, les contrôles environnementaux exécutent du code avant et après les étapes, les scénarios, les fonctionnalités ou l'exécution complète.
Les rôles de chaque fichier étant désormais clairement définis, le framework peut être installé.
Configuration du cadre de test BDD Windows
Installation:
- Téléchargez et installez Python 3 de https://www.python.org/
- Exécutez la commande suivante dans l'invite de commandes pour installer Behave
pip install behave- ICI: PyCharm L'édition communautaire est utilisée ici — https://www.jetbrains.com/pycharm/download/
Configuration du projet :
- Créer un nouveau projet
- Créez la structure de répertoires suivante
La capture d'écran ci-dessus montre la mise en page attendue par Behave : un Caractéristiques répertoire contenant les fichiers de fonctionnalités, et un répertoire imbriqué mesures répertoire contenant le Python implémentations. Behave détecte les deux par leur nom, les noms de dossiers doivent donc correspondre exactement.
Fichiers de fonctionnalités :
Commençons maintenant par créer le fichier de fonctionnalités. Sample_REST_API_Testing.feature, la fonctionnalité étant les opérations CRUD sur le service « posts ».
Cet exemple utilise le https://jsonplaceholder.typicode.com/ Posts est un service REST d'exemple, une API factice gratuite qui accepte les requêtes d'écriture et renvoie des réponses réalistes sans enregistrer les modifications.
Exemple de scénario POST
Scenario: POST post example -> creating a new post item using the 'posts' service Given I set post posts API endpoint -> prerequisite: sets the URL of the posts service When I set HEADER param request content type as "application/json" And set request body And send POST HTTP request -> the actual test step Then I receive valid HTTP response code 201 And Response body "POST" is non-empty -> verification of the response body
De même, vous pouvez écrire les scénarios restants comme suit :
Sample_REST_API_Testing.feature
Feature: Test CRUD methods in Sample REST API testing framework Background: Given I set sample REST API url Scenario: POST post example Given I Set POST posts api endpoint When I Set HEADER param request content type as "application/json" And Set request Body And Send a POST HTTP request Then I receive valid HTTP response code 201 And Response BODY "POST" is non-empty Scenario: GET posts example Given I Set GET posts api endpoint "1" When I Set HEADER param request content type as "application/json" And Send GET HTTP request Then I receive valid HTTP response code 200 for "GET" And Response BODY "GET" is non-empty Scenario: UPDATE posts example Given I Set PUT posts api endpoint for "1" When I Set Update request Body And Send PUT HTTP request Then I receive valid HTTP response code 200 for "PUT" And Response BODY "PUT" is non-empty Scenario: DELETE posts example Given I Set DELETE posts api endpoint for "1" When I Send DELETE HTTP request Then I receive valid HTTP response code 200 for "DELETE"
Étapes de mise en œuvre
Maintenant, pour les étapes fonctionnelles utilisées dans les scénarios ci-dessus, vous pouvez écrire des implémentations dans Python fichiers dans le répertoire « steps ».
Le framework Behave identifie la fonction d'étape en faisant correspondre les décorateurs au prédicat du fichier de fonctionnalités. Par exemple, un Donné Le prédicat dans un scénario de fichier de fonctionnalités recherche une fonction en escalier qui transporte le @given décorateur. La même correspondance s'applique au décorateur. Lorsque vous et EnsuiteDans le cas de « Mais » et « Et », la fonction d'étape adopte le même décorateur que l'étape précédente. Par exemple, si « Et » suit une étape donnée, le décorateur correspondant de la fonction d'étape est : @given.
Par exemple, l'étape When pour POST peut être implémentée comme suit. Notez comment "application/json" est transmis du fichier de fonctionnalités à l'espace réservé entre accolades — c'est ce qu'on appelle la paramétrisation.
# Decorator: the braced name captures a value from the feature file @when(u'I Set HEADER param request content type as "{header_content_type}"') def step_impl(context, header_content_type): # Step implementation: set the content type on the request header request_headers['Content-Type'] = header_content_type
De même, la mise en œuvre des autres étapes de l'étape Python le fichier ressemblera à ceci:
sample_step_implementation.py
from behave import given, when, then, step import requests api_endpoints = {} request_headers = {} response_codes = {} response_texts = {} request_bodies = {} api_url = None @given(u'I set sample REST API url') def step_impl(context): global api_url api_url = 'https://jsonplaceholder.typicode.com' # START POST Scenario @given(u'I Set POST posts api endpoint') def step_impl(context): api_endpoints['POST_URL'] = api_url + '/posts' print('url :' + api_endpoints['POST_URL']) @when(u'I Set HEADER param request content type as "{header_content_type}"') def step_impl(context, header_content_type): request_headers['Content-Type'] = header_content_type # "And" or "But" steps are renamed by behave to match their preceding step @when(u'Set request Body') def step_impl(context): request_bodies['POST'] = {"title": "foo", "body": "bar", "userId": "1"} @when(u'Send POST HTTP request') def step_impl(context): # send the request and save the response object response = requests.post(url=api_endpoints['POST_URL'], json=request_bodies['POST'], headers=request_headers) response_texts['POST'] = response.text print("post response :" + response.text) response_codes['POST'] = response.status_code @then(u'I receive valid HTTP response code 201') def step_impl(context): print('Post rep code ;' + str(response_codes['POST'])) assert response_codes['POST'] == 201 # END POST Scenario # START GET Scenario @given(u'I Set GET posts api endpoint "{id}"') def step_impl(context, id): api_endpoints['GET_URL'] = api_url + '/posts/' + id print('url :' + api_endpoints['GET_URL']) @when(u'Send GET HTTP request') def step_impl(context): response = requests.get(url=api_endpoints['GET_URL'], headers=request_headers) response_texts['GET'] = response.text response_codes['GET'] = response.status_code @then(u'I receive valid HTTP response code 200 for "{request_name}"') def step_impl(context, request_name): print('Get rep code for ' + request_name + ':' + str(response_codes[request_name])) assert response_codes[request_name] == 200 @then(u'Response BODY "{request_name}" is non-empty') def step_impl(context, request_name): print('request_name: ' + request_name) print(response_texts) assert response_texts[request_name] is not None # END GET Scenario # START PUT/UPDATE @given(u'I Set PUT posts api endpoint for "{id}"') def step_impl(context, id): api_endpoints['PUT_URL'] = api_url + '/posts/' + id print('url :' + api_endpoints['PUT_URL']) @when(u'I Set Update request Body') def step_impl(context): request_bodies['PUT'] = {"title": "foo", "body": "bar", "userId": "1", "id": "1"} @when(u'Send PUT HTTP request') def step_impl(context): response = requests.put(url=api_endpoints['PUT_URL'], json=request_bodies['PUT'], headers=request_headers) response_texts['PUT'] = response.text print("update response :" + response.text) response_codes['PUT'] = response.status_code # END PUT/UPDATE # START DELETE @given(u'I Set DELETE posts api endpoint for "{id}"') def step_impl(context, id): api_endpoints['DELETE_URL'] = api_url + '/posts/' + id print('url :' + api_endpoints['DELETE_URL']) @when(u'I Send DELETE HTTP request') def step_impl(context): response = requests.delete(url=api_endpoints['DELETE_URL']) response_texts['DELETE'] = response.text print("DELETE response :" + response.text) response_codes['DELETE'] = response.status_code # END DELETE
⚠️ Remarque : Comparaison des codes d'état avec == plutôt que des is Cela compte. is L'opérateur teste l'identité des objets, et non leur égalité. Python 3.8 et versions ultérieures soulèvent une Avertissement de syntaxe : « is » avec un sens littéral pour ce modèle. Toute assertion écrite comme assert code is 201 devrait être réécrit comme assert code == 201.
Exécution des tests
Le développement du script de test est terminé, nous pouvons donc exécuter les tests. Saisissez la commande suivante dans l'invite de commandes pour exécuter le fichier de fonctionnalités :
:: Run a single feature file with the pretty formatter behave -f pretty features\feature_files_folder\Sample_REST_API_Testing.feature :: Run every feature in the project behave
Les résultats de l'exécution du test s'affichent comme suit :
Affichage du rapport sur la console
L'affichage dans la console est pratique pendant le développement, mais les parties prenantes préfèrent généralement un rapport lisible, ce qu'offre Allure.
Rapports
Commencez par installer le formateur Allure Behave et l'outil en ligne de commande Allure. Le formateur est un Python Le package comprend un outil en ligne de commande, disponible en téléchargement séparé et décrit dans le manuel. Documentation du rapport Allure.
:: 1. Install the formatter pip install allure-behave :: 2. Run the tests and write raw results to a folder behave -f allure_behave.formatter:AllureFormatter -o reports/allure-results features/ :: 3. Render and open the HTML report allure serve reports/allure-results
Ceci génère le rapport de résultats de test dans un format présentable et informatif comme celui-ci :
Rapport de test au format HTML
Rapport de test affichant le résultat du scénario individuel
Une suite fonctionnelle n'est utile que si elle reste lisible à mesure que l'API se développe, et c'est là que la rigueur dans la gestion des fichiers de fonctionnalités prend tout son sens.
Meilleures pratiques pour la rédaction des fichiers de fonctionnalités Behave
Une suite de tests Behave se dégrade rapidement lorsque les scénarios décrivent des clics et des charges utiles plutôt que des comportements. Les pratiques ci-dessous permettent de conserver des fichiers de fonctionnalités lisibles pour les parties prenantes métier tout en préservant…ping le Python couche maintenable par les ingénieurs.
- Décrivez le comportement, pas la mise en œuvre. Écrivez « Étant donné qu'un client dispose d'un solde suffisant », et non « Étant donné que la colonne Solde est égale à 500 ». Le fichier de fonctionnalités décrit le fonctionnement du système ; le fichier d'étapes décrit la méthode de vérification.
- Conservez un comportement par scénario. Un scénario qui génère un code d'état, un corps de réponse et une ligne de base de données correspond en réalité à trois scénarios distincts. Les séparer permet d'identifier précisément la cause des échecs.
- Déplacer la configuration partagée en arrière-plan. L'exemple ci-dessus utilise
Background: Given I set sample REST API urlde sorte que chaque scénario hérite de la base URL sans le répéter. - Paramétrez au lieu de dupliquer. Des espaces réservés entre accolades tels que
"{request_name}"Une seule fonction étape par étape gère les assertions GET, PUT et DELETE, ce qui explique pourquoi l'exemple nécessite beaucoup moins de définitions d'étapes que de scénarios. - Utilisez le schéma de scénario pour les variations de données. Lorsque le même comportement doit être prouvé pour plusieurs entrées, un
Examples:Le tableau est plus clair que les scénarios copiés. - Évitez les dépendances entre les scénarios. Behave ne garantit pas qu'un scénario POST s'exécute avant un scénario GET dans toutes les configurations. Chaque scénario doit créer l'état dont il a besoin.
- Remplacez les variables globales au niveau du module par le contexte. Par souci de concision, cet exemple stocke les points de terminaison et les réponses dans des dictionnaires de modules. Dans les environnements de production, stockez-les sur le serveur Behave.
contextL'objet permet ainsi une réinitialisation propre de l'état entre les scénarios. - Scénarios d'étiquetage pour les exécutions sélectives. Étiquettes telles que
@smokeor@regressionpermettrebehave --tags=@smokece qui permet de maintenir un retour d'information rapide dans le pipeline.
L'application de ces bonnes pratiques dès le premier fichier de fonctionnalités permet à une suite BDD de conserver toute sa valeur, bien au-delà des exemples CRUD initiaux. Les équipes qui étendent cette approche la combinent souvent avec… Cucumber pour les projets JVM ou Postman pour des vérifications exploratoires de l'API.






