| name | finwiz-testing |
| description | FinWiz testing standards using pytest-mock. Use when writing tests, mocking dependencies, or fixing test failures. Includes unittest.mock ban enforcement and CrewAI testing patterns. |
| allowed-tools | ["Read","Edit","Bash"] |
FinWiz Testing Standards
Comprehensive testing standards for FinWiz using pytest-mock exclusively.
Core Testing Principles
1. Mock All External Dependencies
- Always use
pytest-mock (never unittest.mock)
- unittest.mock is BANNED - 4 enforcement layers prevent its use
- Mock APIs, file system, network requests, LLM calls
- No real external calls in tests
- Deterministic test execution
unittest.mock Enforcement (CRITICAL)
unittest.mock is COMPLETELY BANNED from this codebase.
We have 4 layers of enforcement:
- Ruff Linting - TID rules automatically catch unittest.mock imports
- Pre-commit Hook - Blocks commits containing unittest.mock
- Runtime Blocker - Raises ImportError if unittest.mock is imported
- Manual Check -
make check-unittest-mock for verification
Migration Required
from unittest.mock import Mock, patch, MagicMock, AsyncMock
def test_example(mocker):
mock_obj = mocker.patch('module.function')
mock_obj.return_value = 'test'
Standard Test Structure
Arrange-Act-Assert Pattern
def test_should_return_buy_recommendation_when_strong_metrics(mocker):
mock_api = mocker.patch('finwiz.tools.yahoo_finance_tool.get_data')
mock_api.return_value = {'pe_ratio': 15, 'growth': 0.25}
result = analyze_stock('AAPL')
assert result.recommendation == 'BUY'
mock_api.assert_called_once_with('AAPL')
Common Mocking Patterns
Mock API Calls
def test_should_fetch_stock_data_when_valid_ticker(mocker):
mock_get = mocker.patch('finwiz.tools.yahoo_finance_tool.yf.Ticker')
mock_ticker = mocker.Mock()
mock_ticker.info = {'symbol': 'AAPL', 'price': 150.0}
mock_get.return_value = mock_ticker
result = get_stock_data('AAPL')
assert result['symbol'] == 'AAPL'
assert result['price'] == 150.0
mock_get.assert_called_once_with('AAPL')
Mock File Operations
def test_should_read_config_file(mocker):
mock_open = mocker.patch('builtins.open', mocker.mock_open(read_data='{"key": "value"}'))
result = load_config('config.json')
assert result['key'] == 'value'
mock_open.assert_called_once_with('config.json')
Mock Environment Variables
def test_should_use_api_key_from_env(mocker):
mocker.patch.dict('os.environ', {'API_KEY': 'test_key'})
result = get_api_key()
assert result == 'test_key'
Mock Datetime
def test_should_use_current_timestamp(mocker):
mock_now = mocker.patch('datetime.datetime')
mock_now.now.return_value = datetime(2025, 3, 10)
result = generate_report()
assert '2025-03-10' in result.timestamp
CrewAI Testing Standards (CRITICAL)
The Problem with CrewAI Testing
CRITICAL LESSON LEARNED: Do not attempt to unit test full CrewAI crew execution. It leads to hanging tests and is impractical.
The Solution: Test What Matters
Focus unit tests on testable business logic, NOT framework execution:
✅ DO Test
- Configuration loading - Verify YAML files load correctly
- Tool routing logic - Test
get_tools_for_asset_class() method logic
- Input validation - Test parameter validation (asset_class, ticker)
- Method existence - Verify required methods exist
- File existence - Verify configuration files exist
❌ DON'T Test
- Crew execution - Don't call
crew.kickoff() in unit tests
- Agent creation - Don't instantiate agents with
@agent decorator
- Task execution - Don't execute tasks with
@task decorator
- LLM calls - Don't mock OpenAI/LLM responses
- Full workflow - Don't test end-to-end crew workflows
CrewAI Testing Examples
✅ GOOD - Tests configuration and logic
def test_should_load_agent_configurations_from_yaml(self):
"""Fast, focused test of configuration loading."""
import yaml
from pathlib import Path
config_path = Path("src/finwiz/crews/deep_analysis/config/agents.yaml")
with open(config_path) as f:
config = yaml.safe_load(f)
assert "asset_analyst" in config
assert "role" in config["asset_analyst"]
def test_should_validate_asset_class_parameter(self):
"""Test validation logic without instantiating crew."""
valid_asset_classes = ["stock", "etf", "crypto"]
for asset_class in valid_asset_classes:
assert asset_class.lower() in ["stock", "etf", "crypto"]
❌ BAD - Tries to test crew execution
def test_crew_execution(self, mocker):
"""This will hang or timeout!"""
crew = DeepAnalysisCrew()
mocker.patch.object(crew, "asset_analyst", return_value=mock_agent)
result = crew.kickoff(inputs={"ticker": "AAPL", "asset_class": "stock"})
Test Data Generation
Using Faker
from faker import Faker
fake = Faker()
def test_portfolio_analysis():
ticker = fake.stock_symbol()
price = fake.pyfloat(min_value=10, max_value=1000, right_digits=2)
company_name = fake.company()
result = analyze_holding(ticker, price, company_name)
assert result is not None
Test Organization
Directory Structure
tests/
├── unit/ # Unit tests (fast, mocked)
│ ├── tools/
│ ├── crews/
│ ├── schemas/
│ └── utils/
├── integration/ # Integration tests (slow, real APIs)
├── fixtures/ # Shared test fixtures
└── conftest.py # pytest configuration
Test Markers
def test_should_calculate_score():
pass
@pytest.mark.integration
def test_should_fetch_real_stock_data():
pass
@pytest.mark.slow
def test_should_backtest_strategy():
pass
Running Tests
Essential Commands
uv run pytest -m "not integration"
uv run pytest -m integration
uv run pytest tests/unit/tools/test_alternative_finder_tool.py
uv run pytest --cov=src/finwiz --cov-report=html
uv run pytest -x
Quality Checklist
Before committing tests:
Anti-Patterns (Avoid)
❌ Using unittest.mock - BANNED with 4-layer enforcement
❌ Real API calls - Mock all external dependencies
❌ Shared state - Each test should be independent
❌ Long tests - Keep unit tests fast (< 5 seconds)
❌ Testing CrewAI execution - Test configuration and logic only
❌ Unclear names - Use descriptive test names
❌ No assertions - Every test must have assertions
Quick Fixes
unittest.mock Violations
If you see unittest.mock anywhere:
- It's a bug - Report or fix immediately
- Cannot be committed - Pre-commit hook blocks it
- Cannot pass linting - Ruff TID rules catch it
- Cannot run tests - Runtime blocker prevents import
Quick Fix:
from unittest.mock import patch
def test_example(mocker):
mock_obj = mocker.patch('module.function')
Apply these testing standards consistently across all FinWiz test code.