소개
GitHub Copilot은 테스트를 빠르게 개발하고 생산성을 향상시키는 데 도움이 될 수 있습니다. 이 문서에서는 Copilot을 사용하여 단위 및 통합 테스트를 모두 작성하는 방법을 설명합니다. Copilot은 기본 함수에 대한 테스트를 생성할 때 잘 수행되지만 복잡한 시나리오에는 좀 더 자세한 프롬프트와 전략이 필요합니다. 이 문서에서는 Copilot을 사용하여 작업을 분석하고 코드 정확성을 확인하는 실제 예제를 안내합니다.
필수 조건
시작하기 전에 다음을 수행해야 합니다.
- GitHub Copilot 구독 플랜.
- Visual Studio, Visual Studio Code 또는 JetBrains IDE입니다.
- GitHub Copilot 확장이 IDE에 설치되어 있습니다.
Copilot Chat을 사용하여 단위 테스트 작성
이 섹션에서는 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 Chat을 사용하여 단위 테스트 생성
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에서 생성하는 테스트는 모든 시나리오를 다루지 않을 수 있으므로 생성된 코드를 항상 검토하고 필요할 수 있는 추가 테스트를 추가해야 합니다.
Tip
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을 사용하여 통합 테스트 작성
통합 테스트는 결합 시 시스템의 다양한 구성 요소가 올바르게 작동하는지 확인하는 데 필수적입니다. 이 섹션에서는 외부 서비스 NotificationSystem
와의 상호 작용을 포함하고 모의 항목을 사용하여 실제 연결 없이도 시스템 동작을 테스트하도록 BankAccount
클래스를 확장합니다. 통합 테스트의 목표는 BankAccount
클래스와 NotificationSystem
서비스 간의 상호 작용을 확인하여 올바르게 함께 작동하는지 확인하는 것입니다.
예제 클래스: Notification Services가 있는 BankAccount
사용자에게 알림을 보내는 NotificationSystem
와(과) 같은 외부 서비스와의 상호 작용을 포함하도록 BankAccount
클래스를 업데이트 해보겠습니다. 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
)를 명확하게 표시합니다.
Tip
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