Introduction
GitHub Copilot peut vous aider à développer des tests rapidement et à améliorer votre productivité. Dans cet article, nous allons montrer comment vous pouvez utiliser Copilot pour écrire des tests unitaires et d'intégration. Si Copilot donne de bons résultats lors de la génération de tests pour les fonctions de base, les scénarios complexes nécessitent des invites et des stratégies plus détaillées. Cet article présente des exemples pratiques de l'utilisation de Copilot pour décomposer les tâches et vérifier l'exactitude du code.
Prérequis
Avant de commencer, vous devez disposer des éléments suivants :
- Un GitHub Copilot plan d'abonnement.
- Visual Studio, Visual Studio Code, ou n'importe quel IDE de JetBrains.
- L'extension GitHub Copilot installée dans votre IDE.
Écrire des tests unitaires avec Copilot Chat
Dans cette section, nous allons voir comment utiliser GitHub Copilot Chat pour générer des tests unitaires pour une classe Python. Cet exemple montre comment vous pouvez utiliser Copilot pour créer des tests unitaires pour une classe comme BankAccount
. Nous allons vous montrer comment demander Copilot de générer des tests, de les exécuter et de vérifier les résultats.
Exemple de classe : BankAccount
Commençons par une classe BankAccount
qui contient des méthodes pour déposer, retirer et obtenir le solde d'un compte. Créez un fichier bank_account.py
dans un référentiel GitHub et ajoutez la classe BankAccount
suivante dans Python.
class BankAccount:
def __init__(self, initial_balance=0):
if initial_balance < 0:
raise ValueError("Initial balance cannot be negative.")
self.balance = initial_balance
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit amount must be positive.")
self.balance += amount
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Withdrawal amount must be positive.")
if amount > self.balance:
raise ValueError("Cannot withdraw more than the current balance.")
self.balance -= amount
def get_balance(self):
return self.balance
Générer des tests unitaires avec Copilot Chat
Vous pouvez demander à Copilot d'écrire des tests qui couvrent une série de scénarios. Pour obtenir les meilleurs résultats, soyez précis dans les exigences de vos tests. Voici un exemple d'invite que vous pouvez utiliser pour générer des tests unitaires pour la classe BankAccount
.
**Invitate : ** _Développez une suite complète de tests unitaires pour la fonction BankAccount() en Python. Rédiger plusieurs méthodes de test qui couvrent un large éventail de scénarios, y compris les cas limites, la gestion des exceptions et la validation des données _.
Cette invite a plusieurs fonctions importantes :
- Portée : Il est explicitement demandé une suite complète de tests unitaires, de sorte que Copilot génère plus que de simples tests de base, couvrant une variété de cas d'utilisation potentiels.
- Scénarios: l’invite spécifie que les tests doivent inclure :
- cas Edge: teste les conditions limites (par exemple, les valeurs négatives des dépôts ou des retraits, le retrait de plus que le solde disponible).
- Gestion des exceptions : tests des erreurs qui doivent être déclenchées lorsque certaines actions non valables se produisent (par exemple, retrait d'un montant supérieur au solde disponible, dépôt d'un montant négatif).
- validation des données: garantit que les entrées sont correctement validées et gérées par les méthodes.
Copilot vous donnera une réponse comme la suivante.
import unittest
from bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def setUp(self):
self.account = BankAccount()
def test_initial_balance(self):
self.assertEqual(self.account.get_balance(), 0)
def test_deposit_positive_amount(self):
self.account.deposit(100)
self.assertEqual(self.account.get_balance(), 100)
def test_withdraw_within_balance(self):
self.account.deposit(100)
self.account.withdraw(50)
self.assertEqual(self.account.get_balance(), 50)
def test_deposit_negative_amount_raises_error(self):
with self.assertRaises(ValueError):
self.account.deposit(-100)
def test_withdraw_negative_amount_raises_error(self):
with self.assertRaises(ValueError):
self.account.withdraw(-50)
def test_withdraw_more_than_balance_raises_error(self):
self.account.deposit(100)
with self.assertRaises(ValueError):
self.account.withdraw(200)
def test_initial_balance_negative_raises_error(self):
with self.assertRaises(ValueError):
BankAccount(-100)
if __name__ == '__main__':
unittest.main()
Exécution des tests
La suite de tests générée par Copilot comprend un cas de test pour chaque scénario spécifié dans l'invite. Chaque fonction de la classe BankAccount
est testée pour une série de conditions, y compris les opérations valides telles que les dépôts et les retraits, ainsi que les cas limites tels que les montants négatifs et les tentatives de retrait d'un montant supérieur au solde disponible.
Une fois que Copilot a généré la suite de tests à votre satisfaction, ajoutez le code dans un nouveau fichier test_bank_account.py
. Vous pouvez lui demander comment exécuter les tests.
Invite : « Comment exécuter ces tests unitaires en Python à l'aide du framework unittest ?
Copilot vous donnera la commande bash suivante.
python -m unittest test_bank_account.py
Après avoir exécuté les tests, vous verrez les résultats dans votre terminal ou votre IDE. Si tous les tests réussissent, vous pouvez être sûr que votre classe BankAccount
fonctionne comme prévu.
Commande à barre oblique
En outre, vous pouvez demander à Copilot d'écrire une suite complète de tests unitaires à l'aide de la commande slash /tests
. Assurez-vous que le fichier est ouvert dans l'onglet actuel de votre IDE et Copilot générera des tests unitaires pour ce fichier. Les tests générés par Copilot peuvent ne pas couvrir tous les scénarios, c'est pourquoi vous devez toujours revoir le code généré et ajouter les tests supplémentaires nécessaires.
Tip
Si vous demandez à Copilot d'écrire des tests pour un fichier de code qui n'est pas déjà couvert par des tests unitaires, vous pouvez fournir à Copilot un contexte utile en ouvrant un ou plusieurs fichiers de test existants dans des onglets adjacents de votre éditeur. Copilot sera en mesure de voir le cadre de test que vous utilisez et sera plus susceptible d'écrire un test qui est cohérent avec vos tests existants.
Copilot générera une suite de tests unitaires telle que la suivante.
import unittest
from bank_account import BankAccount
class TestBankAccount(unittest.TestCase):
def setUp(self):
self.account = BankAccount()
def test_initial_balance(self):
self.assertEqual(self.account.get_balance(), 0)
Rédaction de tests d'intégration avec Copilot
Les tests d'intégration sont essentiels pour garantir que les différents composants de votre système fonctionnent correctement lorsqu'ils sont combinés. Dans cette section, nous allons étendre notre BankAccount
classe pour y inclure des interactions avec un service externeNotificationSystem
et utiliser des mocks pour tester le comportement du système sans avoir besoin de connexions réelles. L'objectif des tests d'intégration est de vérifier l'interaction entre BankAccount
la classe et les NotificationSystem
services, afin de s'assurer qu'ils fonctionnent correctement ensemble.
Exemple de classe : BankAccount
avec les services de notification
Nous allons mettre à jour la classe BankAccount
pour inclure des interactions avec un service externe tel qu’un NotificationSystem
qui envoie des notifications aux utilisateurs. NotificationSystem
représente l’intégration qui doit être testée.
Mettez à jour la BankAccount
classe dans le fichier bank_account.py
avec l'extrait de code suivant.
class BankAccount:
def __init__(self, initial_balance=0, notification_system=None):
if initial_balance < 0:
raise ValueError("Initial balance cannot be negative.")
self.balance = initial_balance
self.notification_system = notification_system
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit amount must be positive.")
self.balance += amount
if self.notification_system:
self.notification_system.notify(f"Deposited {amount}, new balance: {self.balance}")
def withdraw(self, amount):
if amount <= 0:
raise ValueError("Withdrawal amount must be positive.")
if amount > self.balance:
raise ValueError("Cannot withdraw more than the current balance.")
self.balance -= amount
if self.notification_system:
self.notification_system.notify(f"Withdrew {amount}, new balance: {self.balance}")
def get_balance(self):
return self.balance
Ici, nous allons décomposer notre demande de Copilot pour écrire des tests d’intégration pour la classe BankAccount
en éléments plus petits et plus gérables. Cela aidera Copilot à générer des tests plus précis et plus pertinents.
Invite : « Écrire des tests d’intégration pour la fonction deposit
dans la classe BankAccount
. Utilisez des simulations pour simuler la NotificationSystem
et vérifier qu’elle est appelée correctement après un référentiel. »
Cette invite a plusieurs fonctions importantes :
- étendue: elle spécifie des tests d’intégration, en se concentrant sur l’interaction entre la fonction
deposit
et leNotificationSystem
, plutôt que sur les tests unitaires. - Mocks: il demande explicitement l’utilisation de fictives pour simuler l'
NotificationSystem
, en veillant à ce que l’interaction avec des systèmes externes soit testée sans compter sur leur implémentation réelle. - vérification: l’invite met l’accent sur la vérification que le
NotificationSystem
est appelé correctement après un dépôt, ce qui garantit que l’intégration entre les composants fonctionne comme prévu. - Spécificité: l’invite indique clairement la méthode (
deposit
) et la classe (BankAccount
) à tester.
Tip
Si Copilot produit des tests non valides, fournissez des exemples d'entrées et de sorties pour la fonction que vous souhaitez tester. Cela aidera Copilot à évaluer le comportement attendu de la fonction.
Copilot générera une suite de tests comme la suivante.
import unittest
from unittest.mock import Mock
from bank_account import BankAccount
class TestBankAccountIntegration(unittest.TestCase):
def setUp(self):
self.notification_system = Mock()
def test_deposit_with_notification(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
account.deposit(50)
self.assertEqual(account.get_balance(), 150)
self.notification_system.notify.assert_called_once_with("Deposited 50, new balance: 150")
if __name__ == '__main__':
unittest.main()
Ajoutez le code généré à un nouveau fichier test_bank_account_integration.py
.
Amélioration des cas de test
L'invite ci-dessus a généré un seul scénario de test qui vérifie que le NotificationSystem
est appelé lorsqu'un dépôt valide est effectué. Cependant, elle ne couvre pas les cas où une erreur est soulevée pendant le dépôt. Dans ces scénarios, le NotificationSystem
ne doit pas être appelé. Nous devons ajouter un scénario de test qui traite les dépôts non valides et s'assurer que le système de notification n'est pas déclenché.
Invite : « Ajouter un cas de test pour les montants de dépôt non valides pour vérifier que la fonction déclenche les exceptions correctes et que le NotificationService
n’est pas appelé ».
Copilot générera un cas de test tel que le suivant.
def test_deposit_negative_amount_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.deposit(0)
self.notification_system.notify.assert_not_called()
Poser des questions sur les domaines d'amélioration
Maintenant que nous avons écrit des cas de test pour valider la fonctionnalité d'intégration des dépôts, c'est une excellente occasion de rechercher des améliorations dans la suite de tests. Alors que les tests actuels sont fonctionnels, nous pouvons demander Copilot d'évaluer la couverture du code et de suggérer des domaines d'amélioration.
Invite : « Quels tests supplémentaires doivent être inclus pour garantir une couverture complète de l’intégration entre la classe BankAccount
et le NotificationSystem
? »
L’invite de Copilot avec cette question peut vous aider à identifier les cas de test manquants qui ont peut-être été négligés. Dans cette situation, si nous avons testé les dépôts valides et invalides, nous n'avons pas encore couvert la fonctionnalité de retrait.
Copilot générera une suite de tests mise à jour telle que la suivante.
Cliquez pour développer l'exemple de code généré
import unittest
from unittest.mock import Mock
from bank_account import BankAccount
class TestBankAccountIntegration(unittest.TestCase):
def setUp(self):
self.notification_system = Mock()
def test_deposit_with_notification(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
account.deposit(50)
self.assertEqual(account.get_balance(), 150)
self.notification_system.notify.assert_called_once_with("Deposited 50, new balance: 150")
def test_deposit_negative_amount_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.deposit(-50)
self.notification_system.notify.assert_not_called()
def test_deposit_zero_amount_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.deposit(0)
self.notification_system.notify.assert_not_called()
def test_withdraw_with_notification(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
account.withdraw(30)
self.assertEqual(account.get_balance(), 70)
self.notification_system.notify.assert_called_once_with("Withdrew 30, new balance: 70")
def test_withdraw_exceeding_balance_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.withdraw(150)
self.notification_system.notify.assert_not_called()
def test_withdraw_negative_amount_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.withdraw(-30)
self.notification_system.notify.assert_not_called()
def test_withdraw_zero_amount_raises_error(self):
account = BankAccount(initial_balance=100, notification_system=self.notification_system)
with self.assertRaises(ValueError):
account.withdraw(0)
self.notification_system.notify.assert_not_called()
def test_initial_negative_balance_raises_error(self):
with self.assertRaises(ValueError):
BankAccount(initial_balance=-100, notification_system=self.notification_system)
if __name__ == '__main__':
unittest.main()
Une fois que Copilot a généré la suite de tests à votre satisfaction, exécutez les tests à l'aide de la commande ci-dessous pour vérifier les résultats.
python -m unittest test_bank_account_integration.py