# Написание тестов с помощью GitHub Copilot

Используйте Copilot для создания модульных и интеграции тестов и повышения качества кода.

## Введение

GitHub Copilot помогает быстро разрабатывать тесты и повысить производительность. В этой статье мы покажем, как использовать Copilot для записи модульных и интеграции тестов. Хотя Copilot хорошо работает при создании тестов для базовых функций, сложные сценарии требуют более подробных запросов и стратегий. В этой статье рассматриваются практические примеры использования Copilot для разбиения задач и проверки правильности кода.

## Необходимые компоненты

Прежде чем getting started, у вас должны быть следующее:

* A [GitHub Copilot подписочный план](/ru/copilot/about-github-copilot/subscription-plans-for-github-copilot).
* Visual Studio, Visual Studio Codeили любой интегрированной среды разработки JetBrains.
* Расширение [GitHub Copilot](/ru/copilot/managing-copilot/configure-personal-settings/installing-the-github-copilot-extension-in-your-environment) установлено в вашем IDE.

## Написание модульных тестов с помощью Копилот Чат

В этом разделе мы рассмотрим, как использовать Чат GitHub Copilot для создания модульных тестов для класса Python. В этом примере показано, как использовать Copilot для создания модульных тестов для класса, например `BankAccount`. Мы покажем, как запрашивать Copilot создавать тесты, выполнять их и проверять результаты.

### Пример класса: `BankAccount`

Начнем с класса `BankAccount` , содержащего методы для депозита, вывода и получения баланса учетной записи. Создайте новый файл `bank_account.py` в репозитории GitHub и добавьте следующий класс `BankAccount` на Python.

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

### Создание модульных тестов с помощью Копилот Чат

Вы можете запросить Copilot с запросом на запись тестов, охватывающих ряд сценариев. Будьте конкретными с требованиями к тесту, чтобы получить наилучшие результаты. Ниже приведен пример запроса, который можно использовать для создания модульных `BankAccount` тестов для класса.

```
          **Запрос.**_Разработка комплексного набора модульных тестов для функции BankAccount() в Python. Создание нескольких методов тестирования, охватывающих широкий спектр сценариев, включая пограничные случаи, обработку исключений и проверку данных._
```

В этом запросе выполняется несколько важных действий.

* **Область:** явно запрашивает полный набор модульных тестов, поэтому Copilot создает более чем простые тесты, охватывающие различные потенциальные варианты использования.
* **Сценарии:** запрос указывает, что тесты должны включать:
  * **Пограничные случаи:** тесты для условий границ (например, отрицательные значения для депозитов или вывода, вывод больше, чем доступный баланс).
  * **Обработка исключений:** тесты на наличие ошибок, которые должны возникать при возникновении определенных недопустимых действий (например, отменяя больше, чем доступный баланс, вкладывание отрицательных сумм).
  * **Проверка данных.** Убедитесь, что входные данные правильно проверяются и обрабатываются методами.

Copilot даст ответ, как показано ниже.

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

### Выполнение тестов

Набор тестов, созданный Copilot, включает тестовый случай для каждого сценария, указанного в запросе. Каждая функция в `BankAccount` классе проверяется для ряда условий, включая допустимые операции, такие как депозиты и вывод, а также пограничные случаи, такие как отрицательные суммы и попытки снять больше, чем доступный баланс.

После создания набора тестов Copilot добавьте код в новый файл `test_bank_account.py`. Вы можете спросить его, как выполнить тесты.

```
          **Prompt:**_"How do I запускать эти модульные тесты на Python с использованием фреймворка unittest?"_
```

Copilot предоставит следующую команду Bash.

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

После выполнения тестов вы увидите выходные данные в терминале или интегрированной среде разработки. Если все тесты проходят, вы можете быть уверены, что ваш `BankAccount` класс работает должным образом.

#### Команда с косой чертой

Кроме того, можно заставить Copilot написать полный набор модульных тестов с помощью команды косой `/tests` черты. Убедитесь, что файл открыт на текущей вкладке интегрированной среды разработки и Copilot создаст модульные тесты для этого файла. Тесты, создаваемые Copilot, могут не охватывать все сценарии, поэтому всегда следует просматривать созданный код и добавлять любые дополнительные тесты, которые могут потребоваться.

> \[!TIP] Если вы попросите Copilot написать тесты для файла кода, который еще не охвачен модульными тестами, можно предоставить Copilot с полезным контекстом, открыв один или несколько существующих файлов тестов на смежных вкладках в редакторе. Copilot сможет увидеть используемую платформу тестирования и будет более вероятно написать тест, соответствующий существующим тестам.

Copilot создаст набор модульных тестов, например следующий.

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

## Написание тестов интеграции с помощью Copilot

Тесты интеграции необходимы для обеспечения правильной работы различных компонентов системы при объединении. В этом разделе мы расширим наш `BankAccount` класс, чтобы включить взаимодействие с внешней службой `NotificationSystem` и использовать макеты для тестирования поведения системы без необходимости реальных подключений. Цель тестов интеграции заключается в проверке взаимодействия между классом `BankAccount` и `NotificationSystem` службами, обеспечивая правильную работу вместе.

### Пример класса: `BankAccount` с службами уведомлений

Давайте обновим `BankAccount` класс, чтобы включить взаимодействие с внешней службой, например `NotificationSystem` , которая отправляет уведомления пользователям.
`NotificationSystem` представляет интеграцию, которую необходимо протестировать.

```
          `BankAccount` Обновите класс в файле с помощью следующего `bank_account.py` фрагмента кода.
```

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

Здесь мы разберем наш запрос на Copilot для записи тестов интеграции для `BankAccount` класса в меньшие, более управляемые части. Это поможет Copilot создавать более точные и соответствующие тесты.

```
          **Запрос:**_"Написание тестов интеграции для функции в `deposit` классе. Используйте макеты для `BankAccount` имитации `NotificationSystem` и проверки правильности вызова после депозита"._
```

В этом запросе выполняется несколько важных действий.

* **Область.** Он задает тесты интеграции, фокусируя внимание на взаимодействии между `deposit` функцией и `NotificationSystem`функцией, а не только модульными тестами.
* **Макеты:** явно запрашивает использование макетов для имитации `NotificationSystem`, гарантируя, что взаимодействие с внешними системами проверяется, не полагаясь на их фактическую реализацию.
* **Проверка:** запрос подчеркивает правильность `NotificationSystem` вызова после депозита, гарантируя, что интеграция между компонентами работает должным образом.
* **Специфика.** Запрос четко указывает метод (`deposit`) и класс (`BankAccount`) для проверки.

> \[!TIP] Если Copilot создает недопустимые тесты, укажите примеры входных и выходных данных для функции, которую требуется протестировать. Это поможет Copilot оценить ожидаемое поведение функции.

Copilot создаст набор тестов, как показано ниже.

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

Добавьте созданный код в новый файл `test_bank_account_integration.py`.

### Улучшение тестовых вариантов

Приведенный выше запрос создал один тестовый случай, который проверяет `NotificationSystem` , вызывается при внесении допустимого депозита. Однако он не охватывает случаи, когда во время депозита возникает ошибка. В этих сценариях `NotificationSystem` не следует вызывать. Необходимо добавить тестовый случай, обрабатывающий недопустимые депозиты, и убедиться, что система уведомлений не активируется.

```
          **Запрос:**_"Добавьте тестовый случай для недопустимых сумм депозита, чтобы убедиться, что функция вызывает правильные исключения и не `NotificationService` вызывается"._
```

Copilot создаст тестовый случай, например следующий.

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

### Вопрос о областях улучшения

Теперь, когда мы написали тестовые случаи для проверки функциональности интеграции для депозитов, это отличная возможность для поиска улучшений в наборе тестов. Хотя текущие тесты являются функциональными, мы можем предложить Copilot оценить охват кода и предложить области улучшения.

```
          **Запрос:**_"Какие дополнительные тесты следует включить, чтобы обеспечить полное покрытие интеграции между классом `BankAccount` и `NotificationSystem`?"_
```

Запрос Copilot с этим вопросом может помочь определить отсутствующие тестовые случаи, которые, возможно, были пропущены.
В этой ситуации, пока мы проверили действительные и недопустимые депозиты, мы еще не рассмотрели функциональные возможности вывода.

Copilot создаст обновленный набор тестов, например следующий.

<details>
  <summary>Щелкните, чтобы развернуть полный пример созданного кода</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>

После того как Copilot создал набор тестов для удовлетворения, выполните тесты с помощью команды ниже, чтобы проверить результаты.

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

## Использование Пространства второпилотов для улучшения вариантов тестирования

Пространства второпилотов — это функция, которая позволяет упорядочивать и предоставлять общий доступ к контексту для конкретной задачи с помощью Copilot. Это поможет улучшить релевантность получаемых предложений. Предоставив Copilot больше контекста о вашем project, вы сможете получить лучшие рекомендации по тестированию.

Например, можно создать пространство, включающее:

* Модуль, который вы тестируете (например `payments.js`)
* Текущий набор тестов (например `payments.test.js`)
* Отчет о охвате теста или заметки о том, что отсутствует

В пространстве можно задать такие вопросы, как Copilot:

> Какие тестовые случаи отсутствуют на `payments.test.js` основе логики в `payments.js`?

Или сделайте так:

> Напишите модульный тест для логики `refund.js`возврата, следуя структуре в существующем наборе тестов.

Для получения дополнительной информации об использовании Пространства второпилотов см. [О GitHub Copilot Spaces](/ru/copilot/using-github-copilot/copilot-spaces/about-organizing-and-sharing-context-with-copilot-spaces).