Introdução
GitHub Copilot pode ajudá-lo a desenvolver testes rapidamente e melhorar a produtividade. Neste artigo, demonstraremos como você pode usar Copilot para gravar testes de unidade e integração. Embora Copilot tenha um bom desempenho na geração de testes para funções básicas, cenários complexos exigem prompts e estratégias mais detalhados. Este artigo apresentará exemplos práticos de como usar Copilot para dividir tarefas em partes e verificar a correção do código.
Pré-requisitos
Antes de getting started você deve ter o seguinte:
- Um GitHub Copilot plano de assinatura.
- Visual Studio, Visual Studio Code, ou qualquer IDE da JetBrains.
- A extensão GitHub Copilot instalada no seu IDE.
Escrevendo testes de unidade com Bate-papo do Copilot
Nesta seção, exploraremos como usar Copilot Chat do GitHub para gerar testes de unidade para uma classe de Python. Este exemplo demonstra como você pode usar Copilot para criar testes de unidade para uma classe como BankAccount. Mostraremos como solicitar Copilot a geração de 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 novo arquivo bank_account.py em um GitHub repositório e adicione a seguinte BankAccount classe em 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
Gerando testes de unidade com Bate-papo do Copilot
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.
Este comando realiza algumas ações importantes:
- Escopo: Solicita explicitamente um conjunto abrangente de testes de unidade, para que Copilot gere mais do que apenas testes básicos, cobrindo uma variedade de casos de uso.
- Cenários: a solicitação especifica que os testes devem incluir:
- Casos extremos: testes para condições limite (por exemplo, valores negativos para depósitos ou saques, 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.
Copilot fornecerá uma resposta semelhante à seguinte.
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()
Executar os casos de testes
O conjunto de testes gerado por Copilot inclui um caso de teste para cada cenário especificado no prompt. 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 de Copilot gerar o conjunto de testes para sua satisfação, adicione o código a um novo arquivo test_bank_account.py. Você pode perguntar a ele como executar os testes.
Prompt:"Como executo esses testes de unidade no Python usando o framework unittest?"
Copilot fornecerá o seguinte comando bash.
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 pedir para Copilot escrever um conjunto completo de testes unitários com o comando de barra /tests. Verifique se o arquivo está aberto na guia atual do IDE e Copilot gerará testes de unidade para esse arquivo. Os testes gerados Copilot podem não abranger todos os cenários, portanto, você sempre deve examinar o código gerado e adicionar quaisquer testes adicionais que possam ser necessários.
Dica
Se você solicitar Copilot a gravação de testes para um arquivo de código que ainda não esteja coberto por testes de unidade, você poderá fornecer Copilot um contexto útil abrindo um ou mais arquivos de teste existentes em guias adjacentes em seu editor. Copilot será capaz de ver a estrutura de teste que você usa e terá maior probabilidade de gravar um teste que seja consistente com seus testes existentes.
Copilot gerará uma suíte de testes unitários, como o exemplo 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)
Gravando testes de integração com 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 nossa solicitação para que Copilot escreva testes de integração para a classe BankAccount em partes menores e mais fáceis de gerenciar. Isso ajudará a Copilot 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”.
Este comando realiza algumas ações importantes:
- Escopo: especifica testes de integração, com foco na interação entre a função
deposite 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.
Dica
Se Copilot estiver produzindo testes inválidos, forneça exemplos de entradas e saídas para a função que você deseja testar. Isso ajudará a Copilot avaliar o comportamento esperado da função.
Copilot gerará um conjunto de testes como o seguinte.
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.
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”.
Copilot gerará um caso de teste, como o seguinte.
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 Copilot a avaliação da cobertura de código e sugerir áreas de melhoria.
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.
Copilot gerará um conjunto de testes atualizado, como o seguinte.
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
Usar Espaços do Copilot para melhorar as sugestões de teste
Espaços do Copilot é um recurso que permite organizar e compartilhar o contexto específico da tarefa com Copilot. Isso pode ajudar a aumentar a relevância das sugestões recebidas. Ao fornecer Copilot mais contexto sobre seu projeto, você pode obter melhores sugestões de teste.
Por exemplo, você pode criar um espaço que inclua:
- O módulo que você está testando (como
payments.js) - O conjunto de testes atual (como
payments.test.js) - Um relatório de cobertura de teste ou anotações sobre o que está faltando
Nesse espaço, você pode fazer perguntas Copilot como:
Quais casos de teste estão faltando no
payments.test.jsde acordo com a lógica empayments.js?
Ou:
Escreva um teste unitário para a lógica de reembolso em
refund.js, seguindo a estrutura do conjunto de testes existente.
Para obter mais informações sobre como usar Espaços do Copilot, consulte Sobre o GitHub Copilot Spaces.