From act
Guide Test-Driven Development workflow (Red-Green-Refactor) for new features, bug fixes, and refactoring. Supports both Python (pytest) and Ruby (RSpec). Use when writing tests, implementing features, or following TDD methodology. **PROACTIVE ACTIVATION**: Auto-invoke when implementing features or fixing bugs in projects with test infrastructure. **DETECTION**: Check for tests/ directory, pytest.ini, pyproject.toml with pytest config, spec/ directory, .rspec file, or *_spec.rb files. **USE CASES**: Writing production code, fixing bugs, adding features, legacy code characterization.
How this skill is triggered — by the user, by Claude, or both
Slash command
/act:tddThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- **Refactor**: skill: `refactor` for systematic code improvement during the refactor phase
refactor for systematic code improvement during the refactor phasevitest for JavaScript/TypeScript testing with Vitest frameworkdesign-pattern-adopter for applying design patterns when refactoring test-covered codeGuide for Red-Green-Refactor workflow. Supports Python (pytest) and Ruby (RSpec).
| Python (pytest) | Ruby (RSpec) | |
|---|---|---|
| Naming | test_<fn>_<scenario>_<expected> | describe ".method" / "#method" |
| Structure | Mirror src/ in tests/ | One expectation per it |
| Data | @pytest.fixture + type hints | let / let! |
| Variants | @pytest.mark.parametrize | context blocks |
| Exceptions | pytest.raises(Error, match=) | expect { }.to raise_error |
| Run | uv run pytest | bundle exec rspec |
| Lint | uv run ruff check src/ | bundle exec rubocop |
Write the smallest test that defines the desired behavior. Confirm it fails.
Write minimal production code. Run tests until they pass.
Clean up test and production code. Ensure tests still pass.
Use the
refactorskill for systematic code improvement during this phase.
Follow the Red-Green-Refactor cycle. Start with one test, make it pass, then add the next.
Python:
import pytest
from src.cart import calculate_total, Item
class TestCalculateTotal:
@pytest.mark.parametrize("items,expected", [
([], 0.0),
([Item("Coke", 1.50)], 1.50),
], ids=["empty", "single"])
def test_with_valid_items_returns_total(
self, items: list[Item], expected: float
) -> None:
assert calculate_total(items) == expected
def test_with_negative_price_raises_error(self) -> None:
with pytest.raises(ValueError, match="Price cannot be negative"):
calculate_total([Item("Coke", -1.50)])
Ruby:
RSpec.describe Cart do
describe '#calculate_total' do
subject { described_class.calculate_total(items) }
context 'when cart is empty' do
let(:items) { [] }
it { is_expected.to eq(0.0) }
end
context 'when item has negative price' do
let(:items) { [Item.new(name: 'Coke', price: -1.50)] }
it { expect { subject }.to raise_error(ArgumentError, /Price cannot be negative/) }
end
end
end
Never fix a bug without writing a test first.
Python:
def test_calculate_total_with_negative_price_raises_value_error(self) -> None:
"""Regression test for issue #123: negative prices should raise."""
items = [Item(name="Coke", price=-1.50)]
with pytest.raises(ValueError, match="Price cannot be negative"):
calculate_total(items)
Ruby:
context 'when item has negative price' do
# Regression test for issue #123
let(:items) { [Item.new(name: 'Coke', price: -1.50)] }
it 'raises ArgumentError' do
expect { subject }.to raise_error(ArgumentError, /Price cannot be negative/)
end
end
After each cycle, check for:
Implementation:
Tests:
test_*.py naming, mirror src/ structure# Good
def test_calculate_tax_with_negative_amount_raises_value_error():
pass
# Bad - too vague
def test_calculate_tax(): # ❌
pass
@pytest.fixture
def sample_user() -> dict[str, str]:
"""Provide sample user data."""
return {"id": "123", "name": "John Doe"}
@pytest.mark.parametrize("input,expected", [
(0, 0), (1, 1), (2, 4),
], ids=["zero", "one", "two"])
def test_square(input: int, expected: int) -> None:
assert square(input) == expected
def test_divide_by_zero_raises_error() -> None:
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(10, 0)
def test_fetch_user(mocker) -> None:
mock_get = mocker.patch('requests.get')
mock_get.return_value.json.return_value = {"id": 1}
result = fetch_user(1)
mock_get.assert_called_once_with("https://api.example.com/users/1")
.method) before instance methods (#method)it blockRSpec.describe User do
describe '.find' do
# Class methods first
end
describe '#full_name' do
# Instance methods second
end
end
Use let instead of instance variables:
# Good
let(:user) { described_class.new(name: "John") }
# Bad
before { @user = User.new(name: "John") } # ❌
describe '#full_name' do
subject { user.full_name }
it { is_expected.to eq "John Doe" }
end
# Good
it { is_expected.to be_admin }
# Bad
it "returns true" do
expect(subject.admin?).to be true # ❌
end
# Good
let(:notifier) { instance_double(Notifier) }
# Bad
let(:notifier) { double("notifier") } # ❌
Don't test private methods unless explicitly required. Test the public interface.
uv run pytest # All tests
uv run pytest tests/models/test_user.py -v # Specific file
uv run pytest tests/test_file.py::TestClass::test_fn -vv # Specific test
uv run pytest --cov=src --cov-report=term-missing # With coverage
uv run pytest --durations=10 # Check timing
uv run pytest -m "not slow" # Fast tests only
uv run mypy src/ # Type check
uv run ruff check src/ # Lint
bundle exec rspec # All tests
bundle exec rspec spec/models/user_spec.rb # Specific file
bundle exec rspec spec/models/user_spec.rb:42 # Specific line
bundle exec rspec --format documentation # Doc format
COVERAGE=true bundle exec rspec # With coverage
bundle exec rspec --profile # Check timing
bundle exec rspec --tag ~slow # Fast tests only
bundle exec rubocop # Lint
bundle exec rubocop --autocorrect # Auto-fix
| Python | Ruby | |
|---|---|---|
| Tests pass | uv run pytest | bundle exec rspec |
| Coverage ok | uv run pytest --cov=src | COVERAGE=true bundle exec rspec |
| No lint errors | uv run ruff check src/ | bundle exec rubocop |
| Types check | uv run mypy src/ | — |
refactor skill during the refactor phasenpx claudepluginhub mguinada/ai-coding-toolkit --plugin actGuides implementing Test-Driven Development (TDD) with RED-GREEN-REFACTOR cycle, property-based testing, and mutation testing across any language. Includes when to apply and anti-patterns.
Enforces test-driven development for features, bug fixes, and refactoring. Requires failing tests before any production code, with guidance on test types and spec-to-test mapping.