Introdução
O GitHub Copilot pode ajudar você a desenvolver testes rapidamente e aprimorar a produtividade. Neste artigo, demonstraremos como você pode usar o Copilot para escrever testes de unidade e integração. Embora o Copilot tenha um bom desempenho ao gerar testes para funções básicas, cenários complexos exigem solicitações e estratégias mais detalhadas. Este artigo abordará exemplos práticos de como usar o Copilot para dividir tarefas e verificar a correção do código.
Pré-requisitos
Antes de começar, você precisa ter o seguinte:
- Um plano de assinatura do GitHub Copilot.
- O Visual Studio, o Visual Studio Code ou qualquer IDE do JetBrains.
- A extensão GitHub Copilot instalada no IDE.
Como escrever testes de unidade com o Copilot Chat
Nesta seção, vamos explorar como usar o GitHub Copilot Chat para gerar testes de unidade para uma classe Python. Este exemplo demonstra como usar o Copilot para criar testes de unidade para uma classe como BankAccount
. Vamos mostrar como solicitar ao Copilot para gerar testes, executá-los e verificar os resultados.
Exemplo de classe: BankAccount
Vamos começar com uma classe BankAccount
que contém métodos para depositar, sacar e obter o saldo de uma conta. Crie um arquivo bank_account.py
em um repositório GitHub e adicione a classe BankAccount
a seguir no 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
Como gerar testes de unidade com o Copilot Chat
Você pode solicitar ao Copilot que escreva testes que abrangem uma variedade de cenários. Seja específico com os requisitos de teste para obter os melhores resultados. Veja a seguir um exemplo de solicitação que você pode usar para gerar testes de unidade para a classe BankAccount
.
Solicitação: Desenvolva um conjunto abrangente de testes de unidade para a função BankAccount() no Python. Escreva vários métodos de teste que abrangem uma ampla gama de cenários, incluindo casos de borda, tratamento de exceção e validação de dados.
Essa solicitação realiza algumas ações importantes:
- Escopo: solicita explicitamente um conjunto abrangente de testes de unidade, de modo que o Copilot gere mais do que apenas testes básicos, abrangendo uma variedade de possíveis casos de uso.
- Cenários: a solicitação especifica que os testes devem incluir:
- Casos de borda: testa condições de limite (por exemplo, valores negativos para depósitos ou saques e saques acima do saldo disponível).
- Tratamento de exceção: testa erros que devem ser gerados quando determinadas ações inválidas ocorrem (por exemplo, saques acima do saldo disponível, depósito de valores negativos).
- Validação de dados: garante que as entradas sejam validadas e tratadas corretamente pelos métodos.
O Copilot fornecerá uma resposta semelhante à mostrada a seguir.
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()
Como executar os casos de testes
O conjunto de testes gerado pelo Copilot inclui um caso de teste para cada cenário especificado na solicitação. Cada função da classe BankAccount
é testada em relação a uma variedade de condições, incluindo operações válidas, como depósitos e saques, bem como casos de borda, como valores negativos e tentativas de saque acima do saldo disponível.
Depois que o Copilot tiver gerado o conjunto de testes desejado, adicione o código a um novo arquivo test_bank_account.py
. Você pode perguntar a ele como executar os testes.
Solicitação: “Como fazer para executar esses testes de unidade no Python usando a estrutura unittest?”
O Copilot fornecerá o comando Bash a seguir.
python -m unittest test_bank_account.py
Depois de executar os testes, você verá a saída no terminal ou IDE. Se todos os testes forem aprovados, você poderá ter certeza de que a classe BankAccount
está funcionando conforme o esperado.
Comando barra
Além disso, você pode solicitar ao Copilot que escreva um conjunto completo de testes de unidade com o comando barra "/" /tests
. Verifique se o arquivo está aberto na guia atual do IDE e o Copilot vai gerar testes de unidade para esse arquivo. Os testes gerados pelo Copilot podem não abranger todos os cenários, portanto, você sempre deve revisar o código gerado e adicionar outros testes que possam ser necessários.
Tip
Se você pedir ao Copilot para escrever testes para um arquivo de código que ainda não abordado pelos testes de unidade, forneça ao Copilot um contexto útil abrindo um ou mais arquivos de teste existentes em guias adjacentes no editor. O Copilot poderá ver a estrutura de teste usada e será mais provável que ele escreva um teste consistente com os testes existentes.
O Copilot vai gerar um conjunto de testes de unidade semelhante ao mostrado a seguir.
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)
Como escrever testes de integração com o Copilot
Os testes de integração são essenciais para garantir que os vários componentes do sistema funcionem corretamente quando combinados. Nesta seção, vamos estender a classe BankAccount
para incluir interações com um serviço externo NotificationSystem
e vamos usar simulações para testar o comportamento do sistema sem precisar de conexões reais. O objetivo dos testes de integração é verificar a interação entre a classe BankAccount
e os serviços NotificationSystem
, garantindo que eles trabalhem juntos corretamente.
Exemplo de classe: BankAccount
com serviços de notificação
Vamos atualizar a classe BankAccount
para incluir interações com um serviço externo, como um NotificationSystem
que envia notificações aos usuários. O NotificationSystem
representa a integração que precisará ser testada.
Atualize a classe BankAccount
no arquivo bank_account.py
com o snippet de código a seguir.
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
Aqui, dividiremos a solicitação para o Copilot para escrever testes de integração para a classe BankAccount
em partes menores e mais gerenciáveis. Isso ajudará o Copilot a gerar testes mais precisos e relevantes.
Solicitação: “Escreva testes de integração para a função deposit
na classe BankAccount
. Use simulações para simular o NotificationSystem
e verificar se ele é chamado corretamente após um depósito”.
Essa solicitação realiza algumas ações importantes:
- Escopo: especifica testes de integração, com foco na interação entre a função
deposit
e oNotificationSystem
, em vez de apenas testes de unidade. - Simulações: solicita explicitamente o uso de simulações para simular o
NotificationSystem
, garantindo que a interação com sistemas externos seja testada sem depender da implementação real deles. - Verificação: a solicitação enfatiza a verificação de que o
NotificationSystem
é chamado corretamente após um depósito, garantindo que a integração entre os componentes funcione conforme o esperado. - Especificidade: a solicitação indica claramente o método (
deposit
) e a classe (BankAccount
) a serem testados.
Tip
Se o Copilot estiver produzindo testes inválidos, forneça exemplos de entradas e saídas para a função que deseja testar. Isso ajudará o Copilot a avaliar o comportamento esperado da função.
O Copilot vai gerar um conjunto de testes de unidade semelhante ao mostrado a seguir.
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()
Adicione o código gerado a um novo arquivo test_bank_account_integration.py
.
Como aprimorar os casos de teste
A solicitação acima gerou um caso de teste individual que verifica se o NotificationSystem
é chamado quando um depósito válido é feito. No entanto, ela não abrange casos em que um erro é gerado durante o depósito. Nesses cenários, o NotificationSystem
não deve ser chamado. Precisamos adicionar um caso de teste que trata depósitos inválidos e garantir que o sistema de notificação não seja disparado.
Solicitação: “Adicione um caso de teste para valores de depósito inválidos para verificar se a função gera as exceções corretas e se o NotificationService
não é chamado”.
O Copilot vai gerar um caso de teste semelhante ao mostrado a seguir.
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()
Como perguntar sobre áreas de aprimoramento
Agora que escrevemos os casos de teste para validar a funcionalidade de integração para depósitos, essa é uma ótima oportunidade para procurar aprimoramentos no conjunto de testes. Embora os testes atuais estejam funcionais, podemos solicitar ao Copilot que avalie a cobertura de código e sugira áreas de aprimoramento.
Solicitação: “Quais outros testes devem ser incluídos para garantir a cobertura completa da integração entre a classe BankAccount
e o NotificationSystem
?”
Fazer essa pergunta ao Copilot pode ajudar você a identificar casos de teste ausentes que podem ter sido negligenciados. Nessa situação, embora tenhamos testado depósitos válidos e inválidos, ainda não abordamos a funcionalidade de saques.
O Copilot vai gerar um conjunto de testes atualizado semelhante ao mostrado a seguir.
Clique aqui para expandir o exemplo de código gerado completo
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()
Depois que o Copilot tiver gerado o conjunto de testes desejado, execute os testes com o comando abaixo para verificar os resultados.
python -m unittest test_bank_account_integration.py