Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
import pytest
deftest_addition():
"""Prueba la suma básica."""assert2 + 2 == 4deftest_string_uppercase():
"""Prueba la conversión a mayúsculas."""
text = "hello"assert text.upper() == "HELLO"deftest_list_append():
"""Prueba el append de lista."""
items = [1, 2, 3]
items.append(4)
assert4in items
assertlen(items) == 4
Aserciones
# Igualdadassert result == expected
# Desigualdadassert result != unexpected
# Veracidadassert result # Truthyassertnot result # Falsyassert result isTrue# Exactamente Trueassert result isFalse# Exactamente Falseassert result isNone# Exactamente None# Membresíaassert item in collection
assert item notin collection
# Comparacionesassert result > 0assert0 <= result <= 100# Verificación de tipoassertisinstance(result, str)
# Prueba de excepción (enfoque preferido)with pytest.raises(ValueError):
raise ValueError("mensaje de error")
# Verificar mensaje de excepciónwith pytest.raises(ValueError, match="entrada inválida"):
raise ValueError("entrada inválida proporcionada")
Fixtures
Uso Básico de Fixtures
import pytest
@pytest.fixturedefsample_data():
"""Fixture que proporciona datos de ejemplo."""return {"name": "Alice", "age": 30}
deftest_sample_data(sample_data):
"""Prueba usando el fixture."""assert sample_data["name"] == "Alice"assert sample_data["age"] == 30
Fixture con Setup/Teardown
@pytest.fixturedefdatabase():
"""Fixture con setup y teardown."""# Setup
db = Database(":memory:")
db.create_tables()
db.insert_test_data()
yield db # Proporcionar a la prueba# Teardown
db.close()
deftest_database_query(database):
"""Prueba operaciones de base de datos."""
result = database.query("SELECT * FROM users")
assertlen(result) > 0
Alcances de Fixtures
# Alcance de función (por defecto) - se ejecuta por cada prueba@pytest.fixturedeftemp_file():
withopen("temp.txt", "w") as f:
yield f
os.remove("temp.txt")
# Alcance de módulo - se ejecuta una vez por módulo@pytest.fixture(scope="module")defmodule_db():
db = Database(":memory:")
db.create_tables()
yield db
db.close()
# Alcance de sesión - se ejecuta una vez por sesión de pruebas@pytest.fixture(scope="session")defshared_resource():
resource = ExpensiveResource()
yield resource
resource.cleanup()
Fixture con Parámetros
@pytest.fixture(params=[1, 2, 3])defnumber(request):
"""Fixture parametrizado."""return request.param
deftest_numbers(number):
"""La prueba se ejecuta 3 veces, una por cada parámetro."""assert number > 0
Fixtures Autouse
@pytest.fixture(autouse=True)defreset_config():
"""Se ejecuta automáticamente antes de cada prueba."""
Config.reset()
yield
Config.cleanup()
deftest_without_fixture_call():
# reset_config se ejecuta automáticamenteassert Config.get_setting("debug") isFalse
Conftest.py para Fixtures Compartidos
# tests/conftest.pyimport pytest
@pytest.fixturedefclient():
"""Fixture compartido para todas las pruebas."""
app = create_app(testing=True)
with app.test_client() as client:
yield client
@pytest.fixturedefauth_headers(client):
"""Genera cabeceras de autenticación para pruebas de API."""
response = client.post("/api/login", json={
"username": "test",
"password": "test"
})
token = response.json["token"]
return {"Authorization": f"Bearer {token}"}
Parametrización
Parametrización Básica
@pytest.mark.parametrize("input,expected", [
("hello", "HELLO"),
("world", "WORLD"),
("PyThOn", "PYTHON"),
])deftest_uppercase(input, expected):
"""La prueba se ejecuta 3 veces con diferentes entradas."""assertinput.upper() == expected
Múltiples Parámetros
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])deftest_add(a, b, expected):
"""Prueba la suma con múltiples entradas."""assert add(a, b) == expected
Parametrizar con IDs
@pytest.mark.parametrize("input,expected", [
("valid@email.com", True),
("invalid", False),
("@no-domain.com", False),
], ids=["valid-email", "missing-at", "missing-domain"])deftest_email_validation(input, expected):
"""Prueba validación de email con IDs legibles."""assert is_valid_email(input) is expected
# Ejecutar solo pruebas rápidas
pytest -m "not slow"# Ejecutar solo pruebas de integración
pytest -m integration
# Ejecutar pruebas de integración o lentas
pytest -m "integration or slow"
Configurar Markers en pytest.ini
[pytest]markers =
slow: marca pruebas como lentas
integration: marca pruebas como de integración
unit: marca pruebas como unitarias
Mocking y Patching
Mocking de Funciones
from unittest.mock import patch, Mock
@patch("mypackage.external_api_call")deftest_with_mock(api_call_mock):
"""Prueba con API externa mockeada."""
api_call_mock.return_value = {"status": "success"}
result = my_function()
api_call_mock.assert_called_once()
assert result["status"] == "success"
Mocking de Excepciones
@patch("mypackage.api_call")deftest_api_error_handling(api_call_mock):
"""Prueba manejo de errores con excepción mockeada."""
api_call_mock.side_effect = ConnectionError("Error de red")
with pytest.raises(ConnectionError):
api_call()
api_call_mock.assert_called_once()
Mocking de Context Managers
@patch("builtins.open", new_callable=mock_open)deftest_file_reading(mock_file):
"""Prueba lectura de archivo con open mockeado."""
mock_file.return_value.read.return_value = "contenido del archivo"
result = read_file("test.txt")
mock_file.assert_called_once_with("test.txt", "r")
assert result == "contenido del archivo"
Usar Autospec
@patch("mypackage.DBConnection", autospec=True)deftest_autospec(db_mock):
"""Prueba con autospec para detectar mal uso de API."""
db = db_mock.return_value
db.query("SELECT * FROM users")
db_mock.assert_called_once()
Mock de Propiedades
@pytest.fixturedefmock_config():
"""Crea un mock con una propiedad."""
config = Mock()
type(config).debug = PropertyMock(return_value=True)
type(config).api_key = PropertyMock(return_value="test-key")
return config
Pruebas de Código Asíncrono
Pruebas Async con pytest-asyncio
import pytest
@pytest.mark.asyncioasyncdeftest_async_function():
"""Prueba función async."""
result = await async_add(2, 3)
assert result == 5
Fixture Async
@pytest.fixtureasyncdefasync_client():
"""Fixture async que proporciona cliente de prueba async."""
app = create_app()
asyncwith app.test_client() as client:
yield client
Pruebas de Excepciones
Probar Excepciones Esperadas
deftest_divide_by_zero():
"""Prueba que dividir por cero lanza ZeroDivisionError."""with pytest.raises(ZeroDivisionError):
divide(10, 0)
deftest_custom_exception():
"""Prueba excepción personalizada con mensaje."""with pytest.raises(ValueError, match="entrada inválida"):
validate_input("invalid")
Pruebas con tmp_path
deftest_with_tmp_path(tmp_path):
"""Prueba usando el fixture de ruta temporal de pytest."""
test_file = tmp_path / "test.txt"
test_file.write_text("hello world")
result = process_file(str(test_file))
assert result == "hello world"# tmp_path se limpia automáticamente
classTestUserService:
"""Agrupa pruebas relacionadas en una clase.""" @pytest.fixture(autouse=True)defsetup(self):
"""Setup se ejecuta antes de cada prueba en esta clase."""self.service = UserService()
deftest_create_user(self):
"""Prueba creación de usuario."""
user = self.service.create_user("Alice")
assert user.name == "Alice"deftest_delete_user(self):
"""Prueba eliminación de usuario."""
user = User(id=1, name="Bob")
self.service.delete_user(user)
assertnotself.service.user_exists(1)
Buenas Prácticas
HACER
Seguir TDD: Escribir pruebas antes que el código (rojo-verde-refactorizar)
Probar una sola cosa: Cada prueba debe verificar un único comportamiento
Mockear dependencias externas: No depender de servicios externos
Probar casos borde: Entradas vacías, valores None, condiciones de frontera
Apuntar a 80%+ de cobertura: Enfocarse en rutas críticas
Mantener pruebas rápidas: Usar markers para separar pruebas lentas
NO HACER
No probar implementación: Probar comportamiento, no internos
No usar condicionales complejos en pruebas: Mantener pruebas simples
No ignorar fallos de prueba: Todas las pruebas deben pasar
No probar código de terceros: Confiar en que las bibliotecas funcionan
No compartir estado entre pruebas: Las pruebas deben ser independientes
Configuración de pytest
pytest.ini
[pytest]testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
--strict-markers
--disable-warnings
--cov=mypackage
--cov-report=term-missing
--cov-report=html
markers =
slow: marca pruebas como lentas
integration: marca pruebas como de integración
unit: marca pruebas como unitarias
Ejecutar Pruebas
# Ejecutar todas las pruebas
pytest
# Ejecutar archivo específico
pytest tests/test_utils.py
# Ejecutar prueba específica
pytest tests/test_utils.py::test_function
# Ejecutar con salida detallada
pytest -v
# Ejecutar con cobertura
pytest --cov=mypackage --cov-report=html
# Ejecutar solo pruebas rápidas
pytest -m "not slow"# Ejecutar hasta el primer fallo
pytest -x
# Ejecutar últimas pruebas fallidas
pytest --lf
# Ejecutar pruebas con patrón
pytest -k "test_user"# Ejecutar con depurador al fallar
pytest --pdb
Recuerda: Las pruebas también son código. Mantenlas limpias, legibles y mantenibles. Las buenas pruebas detectan bugs; las excelentes pruebas los previenen.