π§ͺ Testing Guideο
Complete guide for testing ai-slop-gate.
Quick Startο
# Run all tests
python -m pytest ai_slop_gate/tests -v
# Run with coverage
python -m pytest ai_slop_gate/tests \
--cov=ai_slop_gate \
--cov-report=term-missing \
--cov-report=html
# View HTML coverage report
xdg-open htmlcov/index.html # Linux
open htmlcov/index.html # macOS
start htmlcov\index.html # Windows
Test Structureο
ai_slop_gate/
βββ tests/
β βββ __init__.py
β βββ conftest.py # Shared fixtures
β βββ test_cli.py # CLI tests
β βββ test_providers/
β β βββ test_static.py # Static analysis tests
β β βββ test_gemini.py # Gemini provider tests
β β βββ test_groq.py # Groq provider tests
β β βββ test_ollama.py # Ollama provider tests
β βββ test_compliance/
β β βββ test_license.py # License checks
β β βββ test_gdpr.py # GDPR compliance
β β βββ test_supply_chain.py # Supply chain tests
β βββ test_integration/
β βββ test_github.py # GitHub integration
β βββ test_gitlab.py # GitLab integration
β βββ test_cache.py # Cache integration
Running Testsο
All Testsο
python -m pytest ai_slop_gate/tests -v
Specific Test Fileο
# Test static provider
python -m pytest ai_slop_gate/tests/test_providers/test_static.py -v
# Test CLI
python -m pytest ai_slop_gate/tests/test_cli.py -v
Specific Test Functionο
python -m pytest ai_slop_gate/tests/test_cli.py::test_run_static -v
By Markerο
# Only unit tests
python -m pytest -m unit
# Only integration tests
python -m pytest -m integration
# Skip slow tests
python -m pytest -m "not slow"
Test Coverageο
Generate Coverage Reportο
source .venv/bin/activate
python -m pytest ai_slop_gate/tests \
--cov=ai_slop_gate \
--cov-report=term-missing \
--cov-report=html \
--cov-report=xml
View Coverageο
# Terminal report (summary)
python -m pytest --cov=ai_slop_gate --cov-report=term
# HTML report (detailed)
python -m pytest --cov=ai_slop_gate --cov-report=html
xdg-open htmlcov/index.html
# XML report (for CI/CD)
python -m pytest --cov=ai_slop_gate --cov-report=xml
Coverage Targetsο
Component |
Target |
Current |
|---|---|---|
Overall |
80% |
Check |
Core CLI |
90% |
- |
Providers |
75% |
- |
Compliance |
85% |
- |
Cache Integration Testsο
Quick Test (30 seconds)ο
./scripts/quick_cache_test.sh
Expected output:
β
Cache system initialized
β
First run: API called
β
Second run: Cache hit
β
All integration tests passed!
Full Smoke Testο
Requires API keys:
export GEMINI_API_KEY="your-key"
./scripts/verify_cache_v2.sh
Tests:
Cache initialization
First run (API call)
Second run (cache hit)
Cache invalidation
Cache statistics
Test Demo Repositoryο
Test detection on intentionally bad code:
git clone https://github.com/SergUdo/slop_test
cd slop_test
# Run static analysis
python -m ai_slop_gate.cli run --provider static --path .
# Run LLM analysis
python -m ai_slop_gate.cli run --provider gemini --llm-local --path .
Expected: Multiple violations detected (secrets, AI slop, license issues)
Writing Testsο
Unit Test Exampleο
import pytest
from ai_slop_gate.providers.static import StaticProvider
def test_secret_detection():
"""Test that hardcoded secrets are detected."""
code = 'API_KEY = "sk-1234567890abcdef"'
provider = StaticProvider()
results = provider.analyze(code)
assert len(results) > 0
assert any('secret' in r.message.lower() for r in results)
Integration Test Exampleο
import pytest
from ai_slop_gate.cli import run_analysis
@pytest.mark.integration
def test_full_analysis(tmp_path):
"""Test full analysis pipeline."""
# Create test file
test_file = tmp_path / "test.py"
test_file.write_text('eval("malicious_code")')
# Run analysis
results = run_analysis(
provider='static',
path=str(tmp_path)
)
assert results.has_violations()
assert 'eval' in results.violations[0].message
Cache Test Exampleο
import pytest
from ai_slop_gate.cache import LLMCache
def test_cache_hit():
"""Test cache hit on repeated analysis."""
cache = LLMCache()
# First call
result1 = cache.get_or_compute(
key='test_key',
compute_fn=lambda: expensive_llm_call()
)
# Second call (should use cache)
result2 = cache.get_or_compute(
key='test_key',
compute_fn=lambda: expensive_llm_call()
)
assert result1 == result2
assert cache.stats['hits'] == 1
Test Fixturesο
Common Fixtures (conftest.py)ο
import pytest
from pathlib import Path
@pytest.fixture
def test_project(tmp_path):
"""Create a temporary test project."""
project = tmp_path / "test_project"
project.mkdir()
# Create test files
(project / "main.py").write_text("print('hello')")
(project / "policy.yml").write_text("enforcement: advisory")
return project
@pytest.fixture
def mock_api_response():
"""Mock LLM API response."""
return {
'severity': 'high',
'confidence': 0.9,
'message': 'AI-generated code detected'
}
Mocking External APIsο
Mock Gemini APIο
import pytest
from unittest.mock import patch
@pytest.mark.unit
def test_gemini_provider(mock_api_response):
"""Test Gemini provider with mocked API."""
with patch('ai_slop_gate.providers.gemini.call_api') as mock:
mock.return_value = mock_api_response
provider = GeminiProvider()
result = provider.analyze("test code")
assert result.severity == 'high'
mock.assert_called_once()
Mock GitHub APIο
@pytest.mark.integration
def test_github_comment():
"""Test PR comment posting."""
with patch('github.PullRequest.create_comment') as mock:
post_pr_comment(
repo='owner/repo',
pr_id=123,
message='Test comment'
)
mock.assert_called_once()
Performance Testingο
Benchmark Testο
import pytest
import time
@pytest.mark.benchmark
def test_static_analysis_performance(benchmark):
"""Benchmark static analysis speed."""
def analyze():
provider = StaticProvider()
return provider.analyze(large_codebase)
result = benchmark(analyze)
assert result.duration < 5.0 # Must complete in 5s
Cache Performanceο
# Measure cache improvement
./scripts/benchmark_cache.sh
# Expected output:
# Without cache: 15.2s
# With cache: 0.8s
# Speedup: 19x
Test Markersο
Available Markersο
@pytest.mark.unit # Unit tests (fast, no external deps)
@pytest.mark.integration # Integration tests (may need API keys)
@pytest.mark.slow # Slow tests (>5 seconds)
@pytest.mark.benchmark # Performance benchmarks
@pytest.mark.requires_api # Requires API keys
Run by Markerο
# Only fast unit tests
pytest -m "unit and not slow"
# Integration tests only
pytest -m integration
# Skip tests requiring API
pytest -m "not requires_api"
CI/CD Testingο
GitHub Actionsο
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install -e .
pip install pytest pytest-cov
- name: Run tests
run: |
python -m pytest ai_slop_gate/tests \
--cov=ai_slop_gate \
--cov-report=xml \
--cov-report=term
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.xml
GitLab CIο
test:
stage: test
image: python:3.12
script:
- pip install -e .
- pip install pytest pytest-cov
- pytest ai_slop_gate/tests --cov=ai_slop_gate --cov-report=term
coverage: '/TOTAL.*\s+(\d+%)$/'
Test Dataο
Fixtures Locationο
ai_slop_gate/tests/fixtures/
βββ code_samples/
β βββ good_code.py
β βββ bad_code.py
β βββ ai_slop.py
β βββ secrets.py
βββ policies/
β βββ strict.yml
β βββ advisory.yml
β βββ permissive.yml
βββ responses/
βββ gemini_response.json
βββ groq_response.json
Using Test Fixturesο
import pytest
from pathlib import Path
@pytest.fixture
def ai_slop_sample():
"""Load AI slop code sample."""
fixture_path = Path(__file__).parent / 'fixtures' / 'code_samples' / 'ai_slop.py'
return fixture_path.read_text()
def test_ai_slop_detection(ai_slop_sample):
provider = GeminiProvider()
result = provider.analyze(ai_slop_sample)
assert result.has_ai_slop
Debugging Testsο
Run with Verbose Outputο
pytest -vv
Show Print Statementsο
pytest -s
Stop on First Failureο
pytest -x
Run Last Failed Testsο
pytest --lf
Debug with PDBο
def test_something():
import pdb; pdb.set_trace()
assert some_condition
Or:
pytest --pdb # Drop into debugger on failure
Test Best Practicesο
1. Keep Tests Fastο
# β
GOOD: Mock external calls
@patch('ai_slop_gate.providers.gemini.call_api')
def test_provider(mock_api):
mock_api.return_value = {...}
# β BAD: Real API calls in tests
def test_provider():
result = call_real_api() # Slow and flaky
2. Test Edge Casesο
def test_empty_input():
assert analyze("") == []
def test_large_input():
assert analyze("x" * 1_000_000) is not None
def test_unicode():
assert analyze("π code") is not None
3. Use Descriptive Namesο
# β
GOOD
def test_secret_detection_in_environment_variables():
...
# β BAD
def test_secrets():
...
4. One Assertion Per Testο
# β
GOOD
def test_severity_is_high():
assert result.severity == 'high'
def test_confidence_is_above_threshold():
assert result.confidence > 0.8
# β BAD
def test_result():
assert result.severity == 'high'
assert result.confidence > 0.8
assert result.message == 'test'
Troubleshootingο
Issue: βModuleNotFoundErrorβο
Solution: Install in editable mode
pip install -e .
Issue: Tests hang indefinitelyο
Solution: Use timeout
pytest --timeout=60 # 60 second timeout per test
Issue: βNo tests collectedβο
Solution: Check test discovery
# Ensure test files start with "test_"
# Ensure test functions start with "test_"
pytest --collect-only # See what pytest finds
Issue: Cache tests failingο
Solution: Clear cache before tests
rm -rf .ai-slop-cache/
pytest ai_slop_gate/tests/test_integration/test_cache.py
Coverage Goalsο
Priority Areasο
Core CLI (90%+)
Command parsing
Configuration loading
Provider orchestration
Static Providers (85%+)
Secret detection
Dangerous function detection
License checks
LLM Providers (75%+)
API integration
Response parsing
Cache integration
Compliance (85%+)
GDPR checks
License validation
Supply chain audit
Supportο
Testing issues?
Check GitHub Issues
Read Documentation