Execute tests with coverage analysis and automated quality reporting. Use when running unit tests, integration tests, e2e tests, analyzing coverage, or debugging test failures.
Test execution with coverage analysis, interactive gap identification, test generation, and iterative coverage improvement.
# Run all tests
/sc:test
# Unit tests with coverage
/sc:test src/components --type unit --coverage
# Coverage gap analysis - identify untested code
/sc:test --gap-analysis --target 80
# Generate missing tests to reach target coverage
/sc:test --generate --target 80 --module src/services
# Watch mode with auto-fix
/sc:test --watch --fix
# Fix existing failures before adding coverage
/sc:test --fix-first --generate --target 80
# Web search for testing guidance (uses Rube MCP's LINKUP_SEARCH)
/sc:test --linkup --query "pytest asyncio best practices"
This skill liberally uses AskUserQuestion at decision points. Test coverage involves tradeoffs between speed and thoroughness. Validate priorities rather than guessing what the user wants covered.
| Flag | Type | Default | Description |
|---|---|---|---|
--type |
string | all | unit, integration, e2e, all |
--coverage |
bool | false | Generate coverage report |
--watch |
bool | false | Continuous watch mode |
--fix |
bool | false | Auto-fix simple failures |
--gap-analysis |
bool | false | Identify coverage gaps without generating tests |
--generate |
bool | false | Generate missing tests to reach target |
--target |
int | 80 | Coverage percentage target |
--module |
string | - | Restrict scope to a specific module |
--fix-first |
bool | false | Fix existing failures before generating new tests |
--dry-run |
bool | false | Show gap analysis and test plan without writing |
--linkup |
bool | false | Web search for guidance (via Rube MCP) |
--query |
string | - | Search query for LINKUP_SEARCH |
| Tool | When to Use | Purpose |
|---|---|---|
mcp__pal__debug |
Test failures | Root cause analysis for failing tests |
mcp__pal__codereview |
Test quality | Review test coverage and quality |
mcp__pal__thinkdeep |
Complex failures | Multi-stage investigation of flaky tests |
mcp__pal__consensus |
Test strategy | Multi-model validation of testing approach |
mcp__pal__apilookup |
Framework docs | Get current testing framework documentation |
# Debug failing test
mcp__pal__debug(
step="Investigating intermittent test failure",
hypothesis="Race condition in async setup",
confidence="medium",
relevant_files=["/tests/test_api.py"]
)
# Review test quality
mcp__pal__codereview(
review_type="full",
findings="Test coverage, assertion quality, edge cases",
focus_on="test isolation and mocking patterns"
)
# Validate testing strategy
mcp__pal__consensus(
models=[{"model": "gpt-5.2", "stance": "neutral"}, {"model": "gemini-3-pro", "stance": "neutral"}],
step="Evaluate: Is integration testing sufficient for this feature?"
)
| Tool | When to Use | Purpose |
|---|---|---|
mcp__rube__RUBE_SEARCH_TOOLS |
CI/CD integration | Find test reporting tools |
mcp__rube__RUBE_MULTI_EXECUTE_TOOL |
Notifications | Post results to Slack, update tickets |
mcp__rube__RUBE_REMOTE_WORKBENCH |
Bulk processing | Analyze large test result sets |
# Search for testing best practices (--linkup flag uses LINKUP_SEARCH)
mcp__rube__RUBE_MULTI_EXECUTE_TOOL(tools=[
{"tool_slug": "LINKUP_SEARCH", "arguments": {
"query": "pytest fixtures best practices",
"depth": "deep",
"output_type": "sourcedAnswer"
}}
])
# Post test results to Slack
mcp__rube__RUBE_MULTI_EXECUTE_TOOL(tools=[
{"tool_slug": "SLACK_SEND_MESSAGE", "arguments": {
"channel": "#ci-results",
"text": "Test run complete: 95% pass rate, 87% coverage"
}}
])
This skill requires evidence. You MUST:
--coverage used| Type | What It Tests | Markers |
|---|---|---|
| Unit | Single function/class in isolation, mocked dependencies | (none or framework default) |
| Integration | Multiple components together, real DB via fixtures | @pytest.mark.integration, describe("integration") |
| E2E | Full pipeline with real or mocked external APIs | @pytest.mark.slow, @pytest.mark.e2e |
Detect the test framework and run coverage:
# Python (pytest)
pytest --cov=src --cov-report=term-missing --cov-report=json:coverage.json -q --tb=no
# JavaScript (jest/vitest)
npx jest --coverage --coverageReporters=json-summary
# or: npx vitest run --coverage
# Go
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
# Rust
cargo tarpaulin --out json
Read the coverage report to extract:
If --module is provided, filter coverage data to the specified module only.
Create a ranked list of files by coverage gap (lowest coverage first):
## Coverage Gap Report
**Current Coverage**: 52% | **Target**: 80% | **Gap**: 28%
| File | Coverage | Missing Lines | Priority |
|------|----------|---------------|----------|
| src/services/payment.py | 0% | 1-120 | CRITICAL |
| src/utils/validator.py | 15% | 12-45, 67-89 | HIGH |
| src/api/routes/users.py | 42% | 55-70, 88-102 | MEDIUM |
Priority rules:
After building the gap report, ask the user what to prioritize:
AskUserQuestion:
question: "Found <N> files below target. Which should I tackle first?"
header: "Priority"
multiSelect: false
options:
- label: "Highest-impact first (Recommended)"
description: "<N CRITICAL + M HIGH priority files -- start with 0% coverage modules>"
- label: "Quick wins first"
description: "Start with files that need only 1-2 tests to reach target"
- label: "Specific module"
description: "I want to focus on a specific area of the codebase"
- label: "Dry run only"
description: "Just show me the gap report -- don't write any tests yet"
If "Specific module": Ask which module to focus on. If "Dry run only": Present the gap report and stop.
Skip these files by default:
__init__.py / index.ts with only re-exportsBefore writing ANY tests, study the project's conventions.
Discover and read test setup files:
| Framework | Files to Read |
|---|---|
| pytest | conftest.py, pyproject.toml [tool.pytest] |
| jest | jest.config.*, setupTests.*, __mocks__/ |
| vitest | vitest.config.*, setup.* |
| Go | *_test.go helpers, testdata/ |
| Rust | tests/common/mod.rs |
For each gap file, find the closest existing test as a template:
Map available test fixtures to their use cases. Build a reference table:
| Fixture/Helper | Purpose | Used By |
|----------------|---------|---------|
| db_session | Database access | Integration tests |
| mock_api_client | Mock external API | Unit tests |
| ... | ... | ... |
| Function Characteristic | Test Type |
|---|---|
| Pure function (no I/O, no DB) | Unit test |
| Uses validation only | Unit test |
| Calls database/ORM | Integration test (needs DB fixture) |
| Calls external API | Unit test with mock |
| HTTP endpoint handler | Integration test |
| Full pipeline execution | E2E test |
Process gaps in priority order (CRITICAL first). For each file:
Identify:
Follow the AAA pattern (Arrange/Act/Assert):
# Python example
class TestFunctionName:
"""Tests for function_name."""
# Happy path
async def test_returns_expected_result(self):
"""function_name returns correct output for valid input."""
# Arrange
input_data = ...
# Act
result = await function_under_test(input_data)
# Assert
assert result.field == expected_value
# Edge cases
async def test_handles_empty_input(self):
"""function_name handles empty input correctly."""
...
# Error conditions (Let It Crash - verify errors propagate)
async def test_raises_on_invalid_input(self):
"""function_name raises ValueError for invalid input."""
with pytest.raises(ValueError):
await function_under_test(invalid_input)
// TypeScript/Jest example
describe('functionName', () => {
it('returns expected result for valid input', () => {
// Arrange
const input = { ... };
// Act
const result = functionName(input);
// Assert
expect(result.field).toBe(expectedValue);
});
it('throws on invalid input', () => {
expect(() => functionName(invalidInput)).toThrow();
});
});
// Go example
func TestFunctionName(t *testing.T) {
t.Run("returns expected result", func(t *testing.T) {
// Arrange
input := ...
// Act
result, err := FunctionName(input)
// Assert
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result.Field != expected {
t.Errorf("got %v, want %v", result.Field, expected)
}
})
}
Rules for all languages:
For tests requiring database or multiple components:
@pytest.mark.integration, tagged describe blocks)For full pipeline or API flow tests:
# Run just the newly created/modified test file
pytest tests/test_<area>/test_<module>.py -v --tb=short
npx jest tests/<module>.test.ts --verbose
go test -v -run TestNewFunction ./pkg/...
If tests fail:
After all new tests pass individually:
# Full test suite - ensure no regressions
pytest -v --tb=short
npx jest --verbose
go test ./...
# Python
ruff check tests/ --fix && ruff format tests/
# TypeScript/JavaScript
npx eslint tests/ --fix && npx prettier tests/ --write
# Go
gofmt -w *_test.go
Run the same coverage command from Phase 1 to get updated metrics.
Report coverage delta:
## Coverage Report
| Metric | Before | After | Delta |
|--------|--------|-------|-------|
| Total Coverage | 52% | 78% | +26% |
| Files with 0% | 5 | 1 | -4 |
### Per-File Improvements
| File | Before | After | Tests Added |
|------|--------|-------|-------------|
| src/services/payment.py | 0% | 85% | 8 |
| src/utils/validator.py | 15% | 72% | 5 |
If coverage is still below target, ask the user rather than auto-iterating:
AskUserQuestion:
question: "Coverage is at <M%> (target: <N%>). Want to continue adding tests?"
header: "Iterate"
multiSelect: false
options:
- label: "Continue -- add more tests"
description: "<K files remaining below target -- next batch would cover <list>"
- label: "Good enough -- stop here"
description: "Accept current coverage and move on"
- label: "Raise the target"
description: "Current coverage exceeded expectations -- set a higher target"
- label: "Switch focus"
description: "Cover a different area instead of continuing the current batch"
If continuing:
Maximum iterations: 5 (per --loop convention).
Present final results:
## Test Coverage Update Summary
**Target**: N%
**Achieved**: M%
**Tests Added**: X unit, Y integration, Z e2e
### New Test Files Created
- tests/test_<area>/test_<module>.py (N tests)
- ...
### Modified Test Files
- tests/test_<area>/test_<module>.py (+N tests)
- ...
### Coverage by Category
| Category | Coverage | Status |
|----------|----------|--------|
| src/services/ | ??% | OK/NEEDS WORK |
| src/routers/ | ??% | OK/NEEDS WORK |
| src/models/ | ??% | OK/NEEDS WORK |
### Remaining Gaps (if any)
- src/services/transformer.py - Excluded (needs full pipeline data)
- ...
### Verification Commands
<framework-specific commands to re-run tests and coverage>
time.sleep() for async synchronizationif x is not None checks (test the contract)When only --coverage is enabled (without --generate):