Skip to main content

使用 GitHub Copilot 编写测试

使用 Copilot 生成单元和集成测试,并帮助提高代码质量。

简介

GitHub Copilot 可帮助你快速开发测试并提高工作效率。 本文将演示如何使用 Copilot 编写单元测试和集成测试。 尽管 Copilot 在生成基本函数测试时表现良好,但复杂方案需要更详细的提示和策略。 本文将逐步介绍使用 Copilot 拆分任务并验证代码正确性的实际示例。

先决条件

开始之前,必须具备以下条件:

使用 Copilot 对话助手

编写单元测试

在本部分中,我们将了解如何使用 GitHub Copilot Chat 生成针对 Python 类的单元测试。 此示例演示如何使用 Copilot 创建针对类(例如 BankAccount)的单元测试。 我们将展示如何提示 Copilot 生成测试、执行测试并验证结果。

示例类:BankAccount

让我们从 BankAccount 类开始,该类包含存款、取款和获取帐户余额的方法。 在 GitHub 存储库中创建新文件 bank_account.py,然后在 Python 中添加以下 BankAccount 类。

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 对话助手

生成单元测试

可以通过请求来提示 Copilot 编写涵盖一系列场景的测试。 请明确测试要求,以获得最佳结果。 下面是一个示例提示,可用于生成针对 BankAccount 类的单元测试。

提示:在 Python 中开发一套针对 BankAccount() 函数的全面单元测试。****__ 编写涵盖多种场景(包括边缘事例、异常处理和数据验证)的多种测试方法。

此提示说明一些重要事项:

  • 范围:它明确要求一套全面的单元测试,以便 Copilot 不仅仅生成基本测试,还涵盖各种潜在的用例****。
  • 场景:此提示指定测试应包括****:
    • 边缘事例:边界条件(例如存款或取款金额为负值、取款金额超过可用余额)测试****。
    • 异常处理:针对发生某些无效操作时应引发的错误(例如取款金额超过可用余额、存入负数金额)的测试****。
    • 数据验证:确保通过方法正确验证和处理输入****。

Copilot 将提供如下所示的响应。

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 中。 可以询问它如何运行测试。

提示:“如何使用 unittest 框架在 Python 中运行这些单元测试?”****__

Copilot 将提供以下 bash 命令。

python -m unittest test_bank_account.py

运行测试后,终端或 IDE 会显示输出。 如果所有测试均通过,则可以确信 BankAccount 类按预期运作。

/ 命令

此外,还可以提示 Copilot 使用 /tests 斜杠命令编写一套完整的单元测试。 确保在 IDE 的当前选项卡上打开文件,并且 Copilot 将生成针对该文件的单元测试。 Copilot 生成的测试可能并未涵盖所有场景,因此应始终评审生成的代码并添加可能需要的任何其他测试。

提示

如果要求 Copilot 针对单元测试尚未涵盖的代码文件编写测试,可以通过在编辑器的相邻选项卡中打开一个或多个现有测试文件,为 Copilot 提供有用的上下文。 Copilot 将能够查看你使用的测试框架,并且将更有可能编写与现有测试一致的测试。

Copilot 将生成如下所示的单元测试套件。

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 表示需要测试的集成。

使用下面的代码片段更新 bank_account.py 文件中的 BankAccount 类。

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 生成更准确的相关测试。

提示:“为 BankAccount 类中的 deposit 函数编写集成测试。****__ 使用模拟来模拟 NotificationSystem 并验证在存款后是否对其进行正确调用。”

此提示说明一些重要事项:

  • 范围:它指定集成测试,侧重于 deposit 函数与 NotificationSystem 之间的交互,而不仅仅是单元测试****。
  • 模拟:它明确要求使用模拟来模拟 NotificationSystem,以确保测试与外部系统的交互,而无需依赖其实际实现****。
  • 验证:此提示强调验证在存款后是否正确调用 NotificationSystem,以确保组件之间的集成按预期运作****。
  • 具体性:此提示明确地说明了要测试的方法 (deposit) 和类 (BankAccount)****。

提示

如果 Copilot 正在生成无效的测试,请提供要测试的函数的输入和输出示例。 这将有助于 Copilot 评估函数的预期行为。

Copilot 将生成如下所示的测试套件。

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 将生成如下所示的测试用例。

    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 将生成如下所示的更新测试套件。

单击以展开完整生成的代码示例
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()

在 Copilot 生成令你满意的测试套件后,请使用以下命令运行测试以验证结果。

python -m unittest test_bank_account_integration.py

使用 Copilot Spaces 改进测试建议

Copilot Spaces 是一项功能,可用于组织并与 Copilot 共享特定于任务的上下文。 这有助于提升收到的建议的相关性。 通过为 Copilot 提供更多关于你项目的上下文信息,你可以获得更好的测试建议。

例如,你可以创建一个包含以下内容的空间:

  • 要测试的模块(如 payments.js
  • 当前测试套件(如 payments.test.js
  • 测试覆盖率报告或关于缺失内容的备注

在该空间中,你可以向 Copilot 提出如下问题:

根据 payments.js 中的逻辑,payments.test.js 中缺少哪些测试用例?

或:

请根据现有测试套件的结构,为 refund.js 中的退款逻辑编写一个单元测试。

有关如何使用 Copilot Spaces 的详细信息,请参阅 关于使用 Copilot Spaces 组织和共享上下文

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy