src/maverick/skills/maverick_python_testing/SKILL.md
Python testing best practices with pytest, unittest, and mocking
npx skillsauth add get2knowio/maverick maverick-python-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert guidance for Python testing with pytest, unittest, fixtures, and mocking patterns.
test_*.py or *_test.pysetUp/tearDown)Example:
import pytest
@pytest.fixture
def sample_data():
"""Fixture providing test data."""
return {"key": "value"}
def test_function_with_fixture(sample_data):
"""Test using fixture data."""
result = process(sample_data)
assert result == expected_value
Scope Levels:
function (default) - Per test functionclass - Per test classmodule - Per test modulesession - Per test sessionExample:
@pytest.fixture(scope="module")
def database():
"""Module-scoped database fixture."""
db = Database()
db.connect()
yield db
db.disconnect()
@pytest.fixture
def user(database):
"""Function-scoped user (depends on database)."""
user = database.create_user("[email protected]")
yield user
database.delete_user(user.id)
Single Parameter:
@pytest.mark.parametrize("input,expected", [
(1, 2),
(2, 4),
(3, 6),
])
def test_double(input, expected):
assert double(input) == expected
Multiple Parameters:
@pytest.mark.parametrize("username", ["alice", "bob"])
@pytest.mark.parametrize("role", ["admin", "user"])
def test_permissions(username, role):
"""Runs 4 tests (2x2 combinations)."""
user = User(username, role)
assert user.has_role(role)
import pytest
@pytest.mark.asyncio
async def test_async_function():
result = await async_operation()
assert result == expected
Patch Decorator:
from unittest.mock import patch, MagicMock
@patch('module.external_api_call')
def test_with_mock(mock_api):
mock_api.return_value = {"status": "ok"}
result = function_that_calls_api()
mock_api.assert_called_once_with(expected_args)
assert result == expected
Patch Context Manager:
def test_with_context_manager():
with patch('module.function') as mock_func:
mock_func.return_value = 42
assert call_function() == 42
Mock Attributes:
mock = MagicMock()
mock.method.return_value = "result"
mock.attribute = "value"
# Assertions
mock.method.assert_called()
mock.method.assert_called_with(arg1, arg2)
mock.method.assert_called_once()
assert mock.method.call_count == 3
WRONG - Patching in wrong place:
# File: myapp/module.py
from external import api_call
def my_function():
return api_call()
# File: tests/test_module.py
# WRONG - patches external.api_call, not myapp.module.api_call
@patch('external.api_call')
def test_wrong(mock_api):
my_function() # Doesn't use the mock!
CORRECT:
# CORRECT - patch where it's used, not where it's defined
@patch('myapp.module.api_call')
def test_correct(mock_api):
my_function() # Uses the mock
pytest --cov=myapp --cov-report=html tests/
# pytest.ini or pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "--cov=src --cov-report=term-missing"
project/
├── src/
│ └── myapp/
│ ├── __init__.py
│ └── module.py
└── tests/
├── conftest.py # Shared fixtures
├── unit/
│ └── test_module.py
└── integration/
└── test_workflow.py
# tests/conftest.py
import pytest
@pytest.fixture(scope="session")
def app_config():
"""Application configuration for all tests."""
return {"debug": True, "db": ":memory:"}
@pytest.fixture
def temp_file(tmp_path):
"""Create a temporary file for testing."""
file = tmp_path / "test.txt"
file.write_text("test content")
return file
import pytest
def test_raises_exception():
with pytest.raises(ValueError, match="invalid input"):
function_that_raises("bad")
import warnings
import pytest
def test_warning():
with pytest.warns(DeprecationWarning):
legacy_function()
@pytest.mark.asyncio
async def test_async_generator():
gen = async_generator()
items = [item async for item in gen]
assert len(items) == 3
def test_file_processing(tmp_path):
"""pytest provides tmp_path fixture."""
test_file = tmp_path / "input.txt"
test_file.write_text("test data")
process_file(test_file)
output = (tmp_path / "output.txt").read_text()
assert output == "processed: test data"
import pytest
@pytest.mark.slow
def test_slow_operation():
"""Mark slow tests to skip in quick runs."""
pass
@pytest.mark.integration
def test_database_integration():
"""Mark integration tests separately."""
pass
@pytest.mark.skip(reason="Not implemented yet")
def test_future_feature():
pass
@pytest.mark.skipif(sys.platform == "win32", reason="Unix only")
def test_unix_specific():
pass
@pytest.mark.xfail(reason="Known bug #123")
def test_known_issue():
pass
Run specific markers:
pytest -m "not slow" # Skip slow tests
pytest -m integration # Only integration tests
# BAD - tests internal implementation
def test_cache_uses_dict():
cache = Cache()
assert isinstance(cache._storage, dict)
# GOOD - tests behavior
def test_cache_retrieval():
cache = Cache()
cache.set("key", "value")
assert cache.get("key") == "value"
# BAD
def test_user():
pass
# GOOD
def test_user_creation_with_valid_email():
pass
# BAD - tests multiple unrelated things
def test_everything():
assert func1() == 1
assert func2() == 2
assert func3() == 3
# GOOD - separate tests
def test_func1_returns_one():
assert func1() == 1
def test_func2_returns_two():
assert func2() == 2
# BAD - tests depend on execution order
def test_create_user():
global user_id
user_id = create_user("alice")
def test_get_user():
user = get_user(user_id) # Depends on previous test!
# GOOD - use fixtures for setup
@pytest.fixture
def user_id():
return create_user("alice")
def test_get_user(user_id):
user = get_user(user_id)
development
Rust unsafe code, FFI, and safety invariants
development
Rust testing patterns (unit, integration, property-based)
development
Rust performance optimization and zero-cost abstractions
development
Rust ownership, borrowing, and lifetimes