Escreva testes para seu pacote Python#
Escrever código que testa o código do seu pacote, também conhecido como suíte de testes, é importante para você, que mantém o pacote, para os usuários e para os contribuidores. Suítes de testes são compostas por conjuntos de funções, métodos e classes escritos com a finalidade de garantir que uma parte específica do código funcione conforme o esperado.
Por que escrever testes para seu pacote?#
Os testes funcionam como uma rede de segurança para alterações no código. Eles ajudam você a identificar e corrigir bugs antes que afetem os usuários. Também aumentam a confiança de que alterações feitas por contribuidores não quebrarão funcionalidades existentes.
Escrever testes para seu pacote Python é importante porque permite:
Encontrar erros: Os testes são uma rede de segurança. Quando você faz alterações ou adiciona novos recursos ao pacote, eles podem indicar rapidamente se você quebrou sem querer algo que antes funcionava bem.
Economizar tempo: Imagine ter um botão mágico capaz de verificar automaticamente se seu pacote continua funcionando como deveria. Os testes são como esse botão mágico! Eles executam todas essas verificações por você, poupando seu tempo.
Facilitar a colaboração: Se você trabalha com outras pessoas ou recebe contribuições externas, os testes ajudam todo mundo a manter o mesmo entendimento. Eles explicam como o pacote deve funcionar, facilitando a compreensão do projeto e a contribuição de outras pessoas.
Refatorar sem medo: Refatoração significa melhorar a estrutura do código sem alterar seu comportamento. Os testes dão segurança para fazer essas mudanças; se algo quebrar, as falhas nos testes avisarão você.
Documentar: Os testes servem como exemplos técnicos de uso do seu pacote. Isso pode ser útil para novos contribuidores técnicos que queiram enviar código ao pacote. Eles podem consultar os testes para entender como as partes da funcionalidade do código se encaixam.
Facilitar a manutenção no longo prazo: À medida que seu pacote evolui, os testes garantem que o código continue se comportando conforme o esperado, mesmo com as mudanças feitas ao longo do tempo. Portanto, ao escrever testes, você também ajuda seu eu do futuro.
Facilitar a revisão de pull requests: Ao executar os testes em uma plataforma de CI, como o GitHub Actions, você consegue encontrar problemas e mudanças na base de código sempre que você ou um contribuidor fizer uma alteração. Isso garante que seu software se comporte como você espera.
Testes para casos de borda dos usuários#
Casos de borda se referem a formas inesperadas ou «atípicas» de alguns usuários utilizarem seu pacote. Os testes permitem abordar diversos casos de borda que poderiam prejudicar a funcionalidade do pacote. Por exemplo, o que acontece se uma função espera um dataframe do pandas, mas o usuário fornece um array do numpy? Seu código lida com essa situação de forma adequada, oferecendo um feedback claro, ou deixa os usuários frustrados com uma falha sem explicação?
Nota
Para uma boa introdução a testes, consulte esta lição da Software Carpentry
Exemplos de teste
Digamos que você tenha uma função Python que soma dois números.
def add_numbers(a: float, b: float) -> float:
"""
Add two numbers together and return the result.
Parameters
----------
a : float
The first number to add.
b : float
The second number to add.
Returns
-------
float
The sum of the two numbers.
"""
return a + b
A test to ensure that function runs as you might expect when provided with
different numbers might look like this. Each set of inputs gets its own
readable ID, so a failing case is easy to spot in the pytest output
(for example, test_add_numbers_returns_sum[negative]):
import pytest
@pytest.mark.parametrize(
"first_number, second_number, expected_sum",
[
(2, 3, 5),
(-1, 4, 3),
(0, 0, 0),
],
ids=["positive", "negative", "zero"],
)
def test_add_numbers_returns_sum(first_number, second_number, expected_sum):
"""Test that add_numbers returns the sum of two numbers."""
actual_sum = add_numbers(first_number, second_number)
assert actual_sum == expected_sum, (
f"Expected {expected_sum}, but got {actual_sum}"
)
🧩🐍 Como saber que tipo de teste escrever?#
Ao começar a escrever testes para seu pacote, considere que:
há três tipos de teste em Tipos de teste para pacotes Python que podem orientar seu desenvolvimento.
seus testes devem considerar como um usuário pode usar (e usar de forma incorreta!) seu pacote.
Nota
Esta seção foi adaptada de uma apresentação de Nick Murphy.
Mas o que você deve testar no seu pacote? Veja alguns exemplos a seguir:
Teste alguns casos típicos: Verifique se o pacote funciona como esperado quando os usuários o utilizam. Por exemplo, se o pacote deve somar dois números, teste se o valor resultante dessa soma está correto.
Teste casos especiais: Às vezes, há casos especiais ou atípicos. Por exemplo, se uma função realiza um cálculo específico que pode se tornar problemático próximo ao valor 0, teste-a com a entrada 0 e também com valores próximos.
Teste nos limites esperados e próximos a eles: Se uma função exige um valor maior ou igual a 1, verifique se ela continua funcionando com os valores 1 e 0.999, além de 1.001 (valores próximos à restrição). Garanta que a função falhe de forma controlada ao receber valores inesperados e que o usuário consiga entender facilmente o motivo da falha por meio de uma mensagem de erro útil.
Write tests that are easy to review#
Clear tests help reviewers and future contributors understand what behavior is being checked and why that behavior matters. As you write tests, try to make the main idea of each test visible without requiring readers to reverse-engineer the setup.
Use descriptive test names: Name each test after the behavior, condition, or edge case it checks. For example,
test_add_numbers_accepts_negative_valuestells readers more thantest_add_numbers_2.Keep one main behavior in focus: A test can contain several assertions, but they should support one clear idea. If a test starts checking several unrelated behaviors, split it into smaller tests.
Add comments only where they help: A short comment is useful when a fixture, regression, or unusual edge case is not obvious from the assertion. Avoid comments that repeat the code.
Keep setup, action, and assertion easy to scan: Arrange the test so readers can quickly see what input is prepared, what code is run, and what result is expected.
For example, a test based on the add_numbers function in the
pyOpenSci Python package template
can make its inputs, action, and expected result visible at a glance:
from my_package.example import add_numbers
def test_add_numbers_returns_sum():
first_number = 1
second_number = 2
expected_sum = 3
actual_sum = add_numbers(first_number, second_number)
assert actual_sum == expected_sum
For more guidance on structuring readable tests, see pytest’s anatomy of a test, which explains the arrange, act, assert, and cleanup phases, and the package template’s example unit test.
Próximos passos#
Agora que você entende o que testar e por quê, conheça os três tipos de teste (unitário, de integração e de ponta a ponta) para determinar qual estilo se adapta melhor ao seu pacote. Depois, aprenda a executar seus testes localmente e em integração contínua. Por fim, acompanhe seu progresso com métricas de cobertura de código.