{"meta":{"title":"GitHub Copilot을 사용하여 테스트 작성","intro":"Copilot을 사용하여 단위 및 통합 테스트를 생성하고 코드 품질을 개선합니다.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ko/copilot","title":"GitHub Copilot"},{"href":"/ko/copilot/tutorials","title":"자습서"},{"href":"/ko/copilot/tutorials/write-tests","title":"테스트 작성"}],"documentType":"article"},"body":"# GitHub Copilot을 사용하여 테스트 작성\n\nCopilot을 사용하여 단위 및 통합 테스트를 생성하고 코드 품질을 개선합니다.\n\n## 소개\n\nGitHub Copilot은 테스트를 빠르게 개발하고 생산성을 향상시키는 데 도움이 될 수 있습니다. 이 문서에서는 Copilot을 사용하여 단위 및 통합 테스트를 모두 작성하는 방법을 설명합니다. Copilot은 기본 함수에 대한 테스트를 생성할 때 잘 수행되지만 복잡한 시나리오에는 좀 더 자세한 프롬프트와 전략이 필요합니다. 이 문서에서는 Copilot을 사용하여 작업을 분석하고 코드 정확성을 확인하는 실제 예제를 안내합니다.\n\n## 필수 조건\n\n시작하기 전에 다음을 확인해야 합니다.\n\n* [GitHub Copilot 구독 플랜](/ko/copilot/about-github-copilot/subscription-plans-for-github-copilot).\n* Visual Studio, Visual Studio Code 또는 JetBrains IDE입니다.\n* IDE에 [GitHub Copilot 확장 프로그램](/ko/copilot/managing-copilot/configure-personal-settings/installing-the-github-copilot-extension-in-your-environment)이 설치되어 있어야 합니다.\n\n## 공동 파일럿 채팅를 사용하여 단위 테스트 작성\n\n이 섹션에서는 깃허브 코파일럿 채팅을 사용하여 Python 클래스에 대한 단위 테스트를 생성하는 방법을 살펴봅니다. 이 예제에서는 Copilot을 사용하여 클래스 `BankAccount`에 대한 단위 테스트를 만드는 방법을 보여 줍니다. Copilot을 프롬프트하여 테스트를 생성하고, 실행하고, 결과를 확인하는 방법을 보여 줍니다.\n\n### 예제 클래스: `BankAccount`\n\n계정의 잔액을 입금, 인출 및 가져오는 방법을 포함하는 클래스 `BankAccount`부터 시작해 보겠습니다. GitHub 리포지토리에 새 파일 `bank_account.py` 만들고 Python에서 다음 `BankAccount` 클래스를 추가합니다.\n\n```python\nclass BankAccount:\n    def __init__(self, initial_balance=0):\n        if initial_balance < 0:\n            raise ValueError(\"Initial balance cannot be negative.\")\n        self.balance = initial_balance\n\n    def deposit(self, amount):\n        if amount <= 0:\n            raise ValueError(\"Deposit amount must be positive.\")\n        self.balance += amount\n\n    def withdraw(self, amount):\n        if amount <= 0:\n            raise ValueError(\"Withdrawal amount must be positive.\")\n        if amount > self.balance:\n            raise ValueError(\"Cannot withdraw more than the current balance.\")\n        self.balance -= amount\n\n    def get_balance(self):\n        return self.balance\n```\n\n### 공동 파일럿 채팅를 사용하여 단위 테스트 생성\n\nCopilot에 다양한 시나리오를 포괄하는 테스트를 작성하라는 요청을 보낼 수 있습니다. 최상의 결과를 얻으려면 테스트 요구 사항을 구체적으로 설명합니다. 다음은 `BankAccount` 클래스에 대한 단위 테스트를 생성하는 데 사용할 수 있는 프롬프트 예제입니다.\n\n```\n          **프롬프트:**_Python에서 BankAccount() 함수에 대한 포괄적인 단위 테스트 제품군을 개발합니다. 에지 사례, 예외 처리 및 데이터 유효성 검사를 포함하여 광범위한 시나리오를 다루는 여러 테스트 메서드를 작성합니다._\n```\n\n이 프롬프트는 다음과 같은 몇 가지 중요한 작업을 수행합니다.\n\n* **범위**: Copilot이 다양한 잠재적 사용 사례를 다루는 기본 테스트 이상을 생성하도록 포괄적인 단위 테스트 제품군을 명시적으로 요청합니다.\n* **시나리오**: 프롬프트는 테스트에 다음을 포함하도록 지정합니다.\n  * **에지 사례**: 경계 조건 테스트(예: 예금 또는 인출에 대한 음수 값, 사용 가능한 잔액보다 더 많이 인출).\n  * **예외 처리**: 특정 잘못된 작업이 발생할 때 발생해야 하는 오류에 대한 테스트(예: 사용 가능한 잔액보다 더 많은 금액을 인출하고 음수 금액을 입금).\n  * **데이터 유효성 검사**: 입력의 유효성을 올바르게 검사하고 메서드에서 처리하도록 합니다.\n\nCopilot은 다음과 같은 응답을 제공합니다.\n\n```python\nimport unittest\nfrom bank_account import BankAccount\n\nclass TestBankAccount(unittest.TestCase):\n    def setUp(self):\n        self.account = BankAccount()\n\n    def test_initial_balance(self):\n        self.assertEqual(self.account.get_balance(), 0)\n\n    def test_deposit_positive_amount(self):\n        self.account.deposit(100)\n        self.assertEqual(self.account.get_balance(), 100)\n\n    def test_withdraw_within_balance(self):\n        self.account.deposit(100)\n        self.account.withdraw(50)\n        self.assertEqual(self.account.get_balance(), 50)\n\n    def test_deposit_negative_amount_raises_error(self):\n        with self.assertRaises(ValueError):\n            self.account.deposit(-100)\n\n    def test_withdraw_negative_amount_raises_error(self):\n        with self.assertRaises(ValueError):\n            self.account.withdraw(-50)\n\n    def test_withdraw_more_than_balance_raises_error(self):\n        self.account.deposit(100)\n        with self.assertRaises(ValueError):\n            self.account.withdraw(200)\n\n    def test_initial_balance_negative_raises_error(self):\n        with self.assertRaises(ValueError):\n            BankAccount(-100)\n\nif __name__ == '__main__':\n    unittest.main()\n```\n\n### 테스트 사례 실행\n\nCopilot에서 생성된 테스트 도구 모음에는 프롬프트에 지정된 각 시나리오에 대한 테스트 사례가 포함됩니다.\n`BankAccount` 클래스에서 각 함수는 예금 및 인출과 같은 유효한 작업뿐만 아니라 음수 금액 및 사용 가능한 잔액보다 더 많은 인출 시도와 같은 에지 사례를 포함하여 다양한 조건에 대해 테스트됩니다.\n\nCopilot이 테스트 도구 모음 생성을 완료하면 새 파일 `test_bank_account.py`에 코드를 추가하십시오. 테스트를 실행하는 방법을 물어볼 수 있습니다.\n\n```\n          **Prompt:**_\"unittest 프레임워크를 사용하여 Python에서 이러한 단위 테스트를 어떻게 실행하나요?\"_\n```\n\nCopilot은 다음 bash 명령을 제공합니다.\n\n```bash\npython -m unittest test_bank_account.py\n```\n\n테스트를 실행하면 터미널 또는 IDE에 출력이 표시됩니다. 모든 테스트가 통과하면 `BankAccount` 클래스가 예상대로 작동하는지 확신할 수 있습니다.\n\n#### 슬래시 명령\n\n또한 Copilot에게 `/tests` 슬래시 명령을 사용하여 전체 단위 테스트 모음을 작성하라는 메시지를 표시할 수 있습니다. IDE의 현재 탭에 파일이 열려 있는지 확인하고 Copilot에서 해당 파일에 대한 단위 테스트를 생성합니다. Copilot에서 생성하는 테스트는 모든 시나리오를 다루지 않을 수 있으므로 생성된 코드를 항상 검토하고 필요할 수 있는 추가 테스트를 추가해야 합니다.\n\n> \\[!TIP] Copilot에 단위 테스트에서 아직 다루지 않은 코드 파일에 대한 테스트를 작성하도록 요청하는 경우 편집기에서 인접한 탭에서 하나 이상의 기존 테스트 파일을 열어 유용한 컨텍스트와 함께 Copilot을 제공할 수 있습니다. Copilot은 사용하는 테스트 프레임워크를 볼 수 있으며 기존 테스트와 일치하는 테스트를 작성할 가능성이 높습니다.\n\nCopilot은 다음과 같은 단위 테스트 제품군을 생성합니다.\n\n```python\nimport unittest\nfrom bank_account import BankAccount\n\nclass TestBankAccount(unittest.TestCase):\n    def setUp(self):\n        self.account = BankAccount()\n\n    def test_initial_balance(self):\n        self.assertEqual(self.account.get_balance(), 0)\n```\n\n## Copilot을 사용하여 통합 테스트 작성\n\n통합 테스트는 결합 시 시스템의 다양한 구성 요소가 올바르게 작동하는지 확인하는 데 필수적입니다. 이 섹션에서는 외부 서비스 `BankAccount`와의 상호 작용을 포함하고 모의 항목을 사용하여 실제 연결 없이도 시스템 동작을 테스트하도록 `NotificationSystem` 클래스를 확장합니다. 통합 테스트의 목표는 `BankAccount` 클래스와 `NotificationSystem` 서비스 간의 상호 작용을 확인하여 올바르게 함께 작동하는지 확인하는 것입니다.\n\n### 예제 클래스: Notification Services가 있는 `BankAccount`\n\n사용자에게 알림을 보내는 `BankAccount`와(과) 같은 외부 서비스와의 상호 작용을 포함하도록 `NotificationSystem` 클래스를 업데이트 해보겠습니다.\n`NotificationSystem`은 테스트해야 하는 통합을 나타냅니다.\n\n다음 코드 조각을 사용하여 `BankAccount` 파일의 `bank_account.py` 클래스를 업데이트합니다.\n\n```python\nclass BankAccount:\n    def __init__(self, initial_balance=0, notification_system=None):\n        if initial_balance < 0:\n            raise ValueError(\"Initial balance cannot be negative.\")\n        self.balance = initial_balance\n        self.notification_system = notification_system\n\n    def deposit(self, amount):\n        if amount <= 0:\n            raise ValueError(\"Deposit amount must be positive.\")\n        self.balance += amount\n        if self.notification_system:\n            self.notification_system.notify(f\"Deposited {amount}, new balance: {self.balance}\")\n\n    def withdraw(self, amount):\n        if amount <= 0:\n            raise ValueError(\"Withdrawal amount must be positive.\")\n        if amount > self.balance:\n            raise ValueError(\"Cannot withdraw more than the current balance.\")\n        self.balance -= amount\n\n        if self.notification_system:\n            self.notification_system.notify(f\"Withdrew {amount}, new balance: {self.balance}\")\n\n    def get_balance(self):\n        return self.balance\n```\n\n여기서는 Copilot에 대한 요청을 세분화하여 `BankAccount` 클래스에 대한 통합 테스트를 더 작고 관리하기 쉬운 조각으로 작성합니다. 이렇게 하면 Copilot에서 좀 더 정확하고 관련 있는 테스트를 생성할 수 있습니다.\n\n```\n          **프롬프트:**_ \"`deposit` 클래스의 `BankAccount` 함수에 대한 통합 테스트를 작성합니다. 모의 개체를 사용하여 `NotificationSystem`을(를) 시뮬레이션하고 입금 후에 올바르게 호출되는지 확인합니다.\"_\n```\n\n이 프롬프트는 다음과 같은 몇 가지 중요한 작업을 수행합니다.\n\n* **범위**: 단위 테스트가 아니라 `deposit` 함수와 `NotificationSystem` 간의 상호 작용에 중점을 두고 통합 테스트를 지정합니다.\n* **모의 객체:** 외부 시스템과의 상호 작용을 실제 구현에 의존하지 않고 테스트하기 위해 `NotificationSystem`을(를) 모의 객체로 시뮬레이션하도록 명시적으로 요청합니다.\n* **확인**: 프롬프트는 `NotificationSystem`이 입금 후 올바르게 호출되는지 확인하여 구성 요소 간의 통합이 예상대로 작동하는지 확인합니다.\n* **특이성**: 프롬프트는 테스트할 메서드(`deposit`) 및 클래스(`BankAccount`)를 명확하게 표시합니다.\n\n> \\[!TIP] Copilot이 잘못된 테스트를 생성하는 경우 테스트하려는 함수에 대한 입력 및 출력의 예를 제공합니다. 이렇게 하면 Copilot이 함수의 예상 동작을 평가하는 데 도움이 됩니다.\n\nCopilot은 다음과 같은 테스트 제품군을 생성합니다.\n\n```python\nimport unittest\nfrom unittest.mock import Mock\nfrom bank_account import BankAccount\n\nclass TestBankAccountIntegration(unittest.TestCase):\n    def setUp(self):\n        self.notification_system = Mock()\n\n    def test_deposit_with_notification(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        account.deposit(50)\n        self.assertEqual(account.get_balance(), 150)\n        self.notification_system.notify.assert_called_once_with(\"Deposited 50, new balance: 150\")\n\nif __name__ == '__main__':\n    unittest.main()\n```\n\n생성된 코드를 새 파일 `test_bank_account_integration.py`에 추가합니다.\n\n### 테스트 사례 개선\n\n위의 프롬프트는 `NotificationSystem`이(가) 유효한 입금이 이루어질 때 호출되는 것을 확인하는 단일 테스트 사례를 생성했습니다. 그러나 입금 중에 오류가 발생하는 경우는 다루지 않습니다. 이러한 시나리오에서는 `NotificationSystem`을(를) 호출하면 안 됩니다. 잘못된 예금을 처리하는 테스트 사례를 추가하고 알림 시스템이 트리거되지 않도록 해야 합니다.\n\n```\n          **프롬프트:**_\"함수가 올바른 예외를 발생시키고 `NotificationService`이(가) 호출되지 않는지 확인하기 위해 잘못된 입금 금액에 대한 테스트 사례를 추가합니다.\"_\n```\n\nCopilot은 다음과 같은 테스트 사례를 생성합니다.\n\n```python\n    def test_deposit_negative_amount_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.deposit(0)\n        self.notification_system.notify.assert_not_called()\n```\n\n### 개선 영역에 대한 질문\n\n이제 예금에 대한 통합 기능의 유효성을 검사하기 위해 테스트 사례를 작성했으므로 테스트 도구 모음 내에서 향상된 기능을 검색할 수 있는 좋은 기회입니다. 현재 테스트가 작동하는 동안 Copilot에 코드 검사를 평가하라는 메시지를 표시하고 개선 영역을 제안할 수 있습니다.\n\n```\n          **프롬프트:**_\"`BankAccount` 클래스와 `NotificationSystem` 간의 통합에 대한 전체 검사를 보장하기 위해 어떤 추가 테스트를 포함해야 하나요?\"_\n```\n\n이 질문으로 Copilot에 메시지를 표시하면 간과되었을 수 있는 누락된 테스트 사례를 식별하는 데 도움이 될 수 있습니다.\n이 경우 유효하고 잘못된 예금을 테스트하는 동안 아직 인출 기능을 다루지 않았습니다.\n\nCopilot은 다음과 같이 업데이트된 테스트 제품군을 생성합니다.\n\n<details>\n  <summary>전체 생성된 코드 예제를 확장하려면 클릭</summary>\n\n```python\nimport unittest\nfrom unittest.mock import Mock\nfrom bank_account import BankAccount\n\nclass TestBankAccountIntegration(unittest.TestCase):\n    def setUp(self):\n        self.notification_system = Mock()\n\n    def test_deposit_with_notification(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        account.deposit(50)\n        self.assertEqual(account.get_balance(), 150)\n        self.notification_system.notify.assert_called_once_with(\"Deposited 50, new balance: 150\")\n\n    def test_deposit_negative_amount_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.deposit(-50)\n        self.notification_system.notify.assert_not_called()\n\n    def test_deposit_zero_amount_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.deposit(0)\n        self.notification_system.notify.assert_not_called()\n\n    def test_withdraw_with_notification(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        account.withdraw(30)\n        self.assertEqual(account.get_balance(), 70)\n        self.notification_system.notify.assert_called_once_with(\"Withdrew 30, new balance: 70\")\n\n    def test_withdraw_exceeding_balance_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.withdraw(150)\n        self.notification_system.notify.assert_not_called()\n\n    def test_withdraw_negative_amount_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.withdraw(-30)\n        self.notification_system.notify.assert_not_called()\n\n    def test_withdraw_zero_amount_raises_error(self):\n        account = BankAccount(initial_balance=100, notification_system=self.notification_system)\n        with self.assertRaises(ValueError):\n            account.withdraw(0)\n        self.notification_system.notify.assert_not_called()\n\n    def test_initial_negative_balance_raises_error(self):\n        with self.assertRaises(ValueError):\n            BankAccount(initial_balance=-100, notification_system=self.notification_system)\n\nif __name__ == '__main__':\n    unittest.main()\n```\n\n</details>\n\nCopilot이 만족도에 맞게 테스트 제품군을 생성한 후에는 아래 명령을 사용하여 테스트를 실행하여 결과를 확인합니다.\n\n```bash\npython -m unittest test_bank_account_integration.py\n```\n\n## Copilot 스페이스를 사용하여 테스트 제안 개선\n\nCopilot 스페이스는 Copilot를 사용하여 작업별 컨텍스트를 구성하고 공유할 수 있는 기능입니다. 이렇게 하면 사용자가 받는 제안의 관련성을 개선하는 데 도움이 됩니다. 프로젝트에 대한 더 많은 컨텍스트를 Copilot에 제공하면 더 나은 테스트 제안을 받을 수 있습니다.\n\n예를 들어, 다음과 같은 항목을 포함하는 Space를 만들 수 있습니다.\n\n* 테스트 중인 모듈(예: `payments.js`)\n* 현재 테스트 도구 모음(예: `payments.test.js`)\n* 누락된 항목에 대한 테스트 검사 보고서 또는 메모\n\n이 Space에서 Copilot에게 다음과 같은 질문할 수 있습니다.\n\n>\n\n```\n          `payments.test.js`의 논리를 기반으로 `payments.js`에서 어떤 테스트 사례가 누락되었어?\n```\n\n또는\n\n> 기존 테스트 도구 모음의 구조를 따르면서 `refund.js`의 환불 논리에 대한 단위 테스트를 작성해줘.\n\nCopilot 스페이스 사용에 대한 자세한 내용은 [GitHub Copilot 스페이스 정보](/ko/copilot/using-github-copilot/copilot-spaces/about-organizing-and-sharing-context-with-copilot-spaces)을 참조하세요."}