# Schreiben von Tests mit GitHub Copilot

Verwende Copilot, um Komponenten- und Integrationstests zu generieren und zu einer besseren Codequalität beizutragen.

## Einführung

GitHub Copilot kann dir helfen, Tests schneller zu entwickeln und die Produktivität zu verbessern. In diesem Artikel wird veranschaulicht, wie du mit Copilot Komponenten- und Integrationstests schreibst. Während sich Copilot besonders zum Generieren von Tests für grundlegende Funktionen eignet, erfordern komplexe Szenarios detailliertere Prompts und Strategien. Dieser Artikel enthält praktische Beispiele dafür, wie mit Copilot Aufgaben aufgeschlüsselt und die Codekorrektheit überprüft werden kann.

## Voraussetzungen

Bevor Sie beginnen, müssen Sie Folgendes haben:

* Ein [GitHub Copilot-Abonnementplan](/de/copilot/about-github-copilot/subscription-plans-for-github-copilot).
* Visual Studio, Visual Studio Code oder eine beliebige JetBrains-IDE
* Die [GitHub Copilot Erweiterung](/de/copilot/managing-copilot/configure-personal-settings/installing-the-github-copilot-extension-in-your-environment) ist in Ihrer IDE installiert.

## Unit-Tests mit Copilot-Chat

schreiben

In diesem Abschnitt erfährst du, wie du mit GitHub Copilot Chat Komponententests für eine Python-Klasse generierst. In diesem Beispiel wird veranschaulicht, wie du mit Copilot Komponententests für eine Klasse wie `BankAccount` erstellst. Außerdem erfährst du, wie du Copilot aufforderst, Tests zu generieren, diese 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.

```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
```

### Unit-Tests mit Copilot-Chat

generieren

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:
\*
**Geltungsbereich:** Er fragt explizit nach einer umfassenden Sammlung von Komponententests, damit Copilot mehr als nur grundlegende Tests generiert und viele verschiedene potenzielle Anwendungsfälle abdeckt.
\*
**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.

Die Antwort von Copilot wird der folgenden ähneln.

```python
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 Testsammlung enthält einen Testfall für jedes Szenario, das im Prompt angegeben ist. 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.

Nachdem Copilot die Testsammlung zu deiner Zufriedenheit generiert hat, füge den Code in eine neue `test_bank_account.py`-Datei 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 dir den folgenden Bash-Befehl zurück.

```bash
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 kannst du Copilot auffordern, eine vollständige Sammlung von Komponententests mit dem Slash-Befehl `/tests` zu schreiben. Stelle sicher, dass die Datei in der aktuellen Registerkarte deiner IDE geöffnet ist, und Copilot wird Komponententests für diese Datei generieren. Die Tests, die Copilot generiert, decken unter Umständen nicht alle Szenarios ab. Daher solltest du den generierten Code immer überprüfen und zusätzliche Tests hinzufügen, die ggf. erforderlich sind.

> \[!TIP] Wenn du Copilot bittest, Tests für eine Codedatei zu schreiben, die nicht von Komponententests abgedeckt wird, kannst du nützlichen Kontext für Copilot bereitstellen, indem du mindestens eine bestehende Testdatei in benachbarten Registerkarten in deinem Editor öffnest. Copilot erkennt das verwendete Testframework und kann mit größerer Wahrscheinlichkeit einen Test schreiben, der zu deinen bestehenden Tests passt.

Copilot generiert eine Komponententestsammlung wie die folgende.

```python
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.

```python
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
```

In diesem Fall sollte die Anforderung für Copilot, Integrationstests für die `BankAccount`-Klasse zu schreiben, in kleinere, besser handhabbare Einheiten aufgeteilt werden. So kann Copilot genauere und relevantere Tests 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.

> \[!TIP] Wenn Copilot ungültige Tests erzeugt, stelle Beispiele für Ein- und Ausgaben der Funktion bereit, die du testen möchtest. Dies wird Copilot dabei helfen, das erwartete Verhalten der Funktion zu bewerten.

Copilot generiert eine Testsammlung wie die folgende.

```python
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 wie den folgenden.

```python
    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. Auch wenn die aktuellen Tests ihren Zweck erfüllen, kannst du Copilot auffordern, die Code Coverage zu testen und Verbesserungen 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 Testsammlung wie die folgende.

<details>
  <summary>Klicke hier, um das vollständige generierte Codebeispiel anzuzeigen.</summary>

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

</details>

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.

```bash
python -m unittest test_bank_account_integration.py
```

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

Copilot Räume ist ein Feature, mit dem du aufgabenspezifischen Kontext mit Copilot organisieren und freigeben kannst. Dies kann dazu beitragen, die Relevanz der erhaltenen Vorschläge zu verbessern. Indem Sie Copilot mit mehr Kontext zu Ihrem Projekt versehen, können Sie bessere Testvorschläge erhalten.

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

Im Raum kannst du Copilot Fragen stellen, z. B.:

> 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 findest du unter Copilot Räume, siehe [Über GitHub Copilot Spaces](/de/copilot/using-github-copilot/copilot-spaces/about-organizing-and-sharing-context-with-copilot-spaces).