Skip to main content

Schreiben von Tests mit GitHub Copilot

Wird Copilot verwendet, um Komponenten- und Integrationstests zu generieren und die Codequalität zu verbessern.

Einführung

GitHub Copilot kann Sie bei der schnellen Entwicklung von Tests und der Verbesserung der Produktivität unterstützen. In diesem Artikel zeigen wir, wie Sie mit Copilot sowohl Unit- als auch Integrationstests schreiben können. Während Copilot beim Generieren von Tests für grundlegende Funktionen gut abschneidet, erfordern komplexe Szenarien detailliertere Eingabeaufforderungen und Strategien. In diesem Artikel wird anhand praktischer Beispiele gezeigt, wie Copilot verwendet wird, um Aufgaben zu zerlegen und die Korrektheit von Code zu überprüfen.

Voraussetzungen

Bevor Sie beginnen, müssen Sie Folgendes haben:

Schreiben von Komponententests mit Copilot-Chat

In diesem Abschnitt sehen wir uns an, wie Sie mit GitHub Copilot Gespräch Unit-Tests für eine Python-Klasse erstellen. Dieses Beispiel zeigt, wie Sie Copilot verwenden können, um Unit-Tests für eine Klasse wie BankAccount zu erstellen. Wir zeigen Ihnen, wie Sie Copilot anweisen, Tests zu generieren, auszuführen und die Ergebnisse zu überprüfen.

Beispielklasse: BankAccount

Angenommen, du hast eine Klasse BankAccount, die Methoden zum Einzahlen, Auszahlen und Abrufen des Kontostands enthält. Erstellen Sie eine neue Datei bank_account.py in einem GitHub Repository, und fügen Sie die folgende BankAccount Klasse in Python hinzu.

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

Generieren von Komponententests mit Copilot-Chat

Du kannst Copilot dazu auffordern, Tests zu schreiben, die verschiedene Szenarios abdecken. Achte auf spezifische Testanforderungen, um die besten Ergebnisse zu erzielen. Im Folgenden findest du einen Beispielprompt, mit dem du Komponententests für die BankAccount-Klasse generieren kannst.

Prompt:„Schreibe eine umfassende Sammlung von Komponententests für die BankAccount()-Funktion in Python. Schreibe mehrere Testmethoden, die viele verschiedene Szenarios abdecken, einschließlich Grenzfällen, Ausnahmebehandlung und Datenüberprüfung.“

Wichtige Aspekte dieses Prompts:

  • Bereich: Es fordert explizit eine umfassende Reihe von Komponententests, sodass Copilot mehr als nur grundlegende Tests generiert werden, die eine Vielzahl potenzieller Anwendungsfälle abdecken.
  • Szenarios: Der Prompt gibt an, dass die Tests Folgendes berücksichtigen sollen:
    • Grenzfälle: Es wird auf Grenzbedingungen (z. B. negative Werte für Ein- oder Auszahlungen und Auszahlungen, die den verfügbaren Saldo übersteigen) getestet.
    • Ausnahmebehandlung: Es wird auf Fehler getestet, die ausgelöst werden sollen, wenn bestimmte ungültige Aktionen auftreten (z. B. Auszahlungen, die den verfügbaren Saldo übersteigen und das Einzahlen negativer Beträge).
    • Datenüberprüfung: Es wird sichergestellt, dass Eingaben von den Methoden korrekt validiert und verarbeitet werden.

Copilot liefert Ihnen eine Antwort wie die folgende.

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()

Ausführen der Testfälle

Die von Copilot generierte Testsuite enthält einen Testfall für jedes im Prompt angegebene Szenario. Jede Funktion in der BankAccount-Klasse wird auf verschiedene Bedingungen getestet, einschließlich gültiger Vorgänge wie Ein- und Auszahlungen sowie Grenzfälle wie negative Beträge und versuchte Auszahlungen, die den verfügbaren Saldo übersteigen.

Sobald Copilot die Test-Suite zu Ihrer Zufriedenheit generiert hat, fügen Sie den Code in eine neue Datei test_bank_account.py ein. Du kannst fragen, wie die Tests ausgeführt werden.

Prompt:"Wie führe ich diese Unit-Tests in Python mit dem unittest-Framework aus?"

Copilot gibt Ihnen den folgenden Bash-Befehl.

python -m unittest test_bank_account.py

Nach dem Ausführen der Tests wird dir die Ausgabe im Terminal oder in der IDE angezeigt. Wenn alle Tests erfolgreich abgeschlossen werden, kannst du sicher sein, dass deine BankAccount-Klasse erwartungsgemäß funktioniert.

Slash-Befehl

Darüber hinaus können Sie Copilot mit dem /tests Slash-Befehl anweisen, eine vollständige Suite von Unit-Tests zu schreiben. Stellen Sie sicher, dass Sie die Datei im aktuellen Tab Ihrer IDE geöffnet haben und Copilot Unit-Tests für diese Datei generiert. Die generierten Tests Copilot decken möglicherweise nicht alle Szenarien ab, daher sollten Sie immer den generierten Code überprüfen und zusätzliche Tests hinzufügen, die möglicherweise erforderlich sind.

Tipp

Wenn Sie Copilot bitten, Tests für eine Codedatei zu schreiben, die noch nicht durch Unit-Tests abgedeckt ist, können Sie Copilot nützlichen Kontext liefern, indem Sie eine oder mehrere vorhandene Testdateien in nebeneinanderliegenden Tabs in Ihrem Editor öffnen. Copilot kann das verwendete Testframework sehen und wird wahrscheinlicher einen Test schreiben, der mit ihren vorhandenen Tests konsistent ist.

Copilot generiert eine Komponententestsuite wie die folgende.

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)

Schreiben von Integrationstests mit Copilot

Integrationstests sind unerlässlich, um sicherzustellen, dass die verschiedenen Komponenten deines Systems zusammen ordnungsgemäß funktionieren. In diesem Abschnitt wird die BankAccount-Klasse um Interaktionen mit einem externen Dienst NotificationSystem erweitert. Außerdem wird Systemverhalten mit Modelltests getestet, ohne dass echte Verbindungen erforderlich sind. Ziel der Integrationstests ist es, die Interaktion zwischen der BankAccount-Klasse und den NotificationSystem-Diensten zu prüfen und sicherzustellen, dass sie ordnungsgemäß zusammenarbeiten.

Beispielklasse: BankAccount mit Benachrichtigungsdiensten

Aktualisiere die BankAccount-Klasse, sodass Interaktionen mit einem externen Dienst einbezogen werden, z. B. ein NotificationSystem-Dienst, der Benachrichtigungen an Benutzer sendet. NotificationSystem stellt die Integration dar, die getestet werden muss.

Aktualisiere die BankAccount-Klasse in der bank_account.py-Datei mit dem folgenden Codeschnipsel.

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

Hier werden wir unsere Anforderung Copilot zum Schreiben von Integrationstests für die BankAccount Klasse in kleinere, verwaltbare Teile unterteilen. Dies wird Copilot dabei helfen, genauere und relevantere Tests zu generieren.

Prompt:„Schreibe Integrationstests für die deposit-Funktion in der BankAccount-Klasse. Verwende Modelltests, um NotificationSystem zu simulieren und zu überprüfen, ob der Dienst nach einer Einzahlung richtig aufgerufen wird.“

Wichtige Aspekte dieses Prompts:

  • Geltungsbereich: Es spezifiziert Integrationstests, die sich auf die Interaktion zwischen der deposit-Funktion und NotificationSystem konzentrieren, anstelle von nur Einheitstests.
  • Modelltests: Er fordert explizit die Verwendung von Modelltests, um NotificationSystem zu simulieren und sicherzustellen, dass die Interaktion mit externen Systemen getestet wird, ohne dass diese tatsächlich implementiert werden müssen.
  • Überprüfung: Der Prompt fordert, dass das korrekte Aufrufen der NotificationSystem-Funktion nach einer Einzahlung überprüft werden muss, um sicherzustellen, dass die Integration zwischen den Komponenten erwartungsgemäß funktioniert.
  • Genauigkeit: Der Prompt gibt die zu testende Methode (deposit) und die zu testende Klasse (BankAccount) genau vor.

Tipp

Wenn Copilot ungültige Tests erzeugt, geben Sie Beispiele für Ein- und Ausgaben der Funktion an, die Sie testen möchten. Dies wird Copilot dabei helfen, das erwartete Verhalten der Funktion zu bewerten.

Copilot generiert eine Testsuite wie die folgende.

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()

Füge den generierten Code in eine neue test_bank_account_integration.py-Datei ein.

Verbessern der Testfälle

Der oben genannte Prompt hat einen einzigen Testfall generiert, der überprüft, ob NotificationSystem nach einer gültigen Einzahlung aufgerufen wird. Jedoch deckt es keine Fälle ab, in denen während der Einzahlung ein Fehler ausgelöst wird. In diesen Szenarios sollte NotificationSystem nicht aufgerufen werden. Daher sollte ein Testfall hinzugefügt werden, der ungültige Einzahlungen verarbeitet und sicherstellt, dass das Benachrichtigungssystem nicht ausgelöst wird.

Prompt:„Füge einen Testfall für ungültige Einzahlungsbeträge hinzu, um sicherzustellen, dass die Funktion die richtigen Ausnahmen auslöst und dass NotificationService nicht aufgerufen wird.“

Copilot generiert einen Testfall, z. B. folgendes.

    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()

Fragen zu Verbesserungspotenzialen

Da wir nun Testfälle geschrieben haben, um die Integrationsfunktionalität für Einzahlungen zu überprüfen, ist dies eine großartige Gelegenheit, nach Verbesserungen innerhalb der Testreihe zu suchen. Während die aktuellen Tests funktionsfähig sind, können wir Copilot dazu auffordern, die Codeabdeckung zu bewerten und Verbesserungspotenziale vorzuschlagen.

Prompt:„Welche zusätzlichen Tests sollten einbezogen werden, um die Integration zwischen der BankAccount-Klasse und NotificationSystem vollständig abzudecken?“

Copilot diese Frage zu stellen, kann dir helfen, fehlende Testfälle zu ermitteln, die möglicherweise übersehen wurden. Im vorliegenden Beispiel wurden zwar gültige und ungültige Einzahlungen getestet, jedoch nicht die Auszahlungsfunktion.

Copilot generiert eine aktualisierte Testsuite wie die folgende.

Klicke hier, um das vollständige generierte Codebeispiel anzuzeigen.
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()

Wenn du mit der von Copilot generierten Testsammlung zufrieden bist, führe die Tests mit dem folgenden Befehl aus, um die Ergebnisse zu überprüfen.

python -m unittest test_bank_account_integration.py

Verwenden von Copilot Räume zur Verbesserung von Testvorschlägen

Copilot Räume ist eine Funktion, mit der Sie aufgabenspezifischen Kontext organisieren und mit Copilot teilen können. Dies kann dazu beitragen, die Relevanz der erhaltenen Vorschläge zu verbessern. Indem Sie Copilot mehr Kontext zu Ihrem Projekt bereitstellen, können Sie bessere Testvorschläge bekommen.

Du kannst z. B. einen Raum erstellen, der Folgendes umfasst:

  • Das Modul, das du testest (z. B. payments.js)
  • Die aktuelle Testsuite (z. B. payments.test.js)
  • Ein Testabdeckungsbericht oder Hinweise zu fehlenden Aspekten

In diesem Bereich können Sie Copilot Fragen stellen wie:

Welche Testfälle fehlen in payments.test.js basierend auf der Logik in payments.js?

Oder:

Schreibe einen Komponententest für die Erstattungslogik in refund.js, nach der Struktur in der vorhandenen Testsuite.

Weitere Informationen zur Verwendung Copilot Räumefinden Sie unter Über GitHub Copilot Spaces.