Guides test-driven development workflow with red-green-refactor cycles. Use when user wants to practice TDD, write tests first, or needs help with test-first development approach.
Installs into .claude/skills of the current project.
Are you the author of tdd-workflow?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/a-ariff-tdd-workflow)
---
name: tdd-workflow
description: Guides test-driven development workflow with red-green-refactor cycles. Use when user wants to practice TDD, write tests first, or needs help with test-first development approach.
allowed-tools: [Read, Write, Edit, Bash, Glob, Grep]
---
# TDD Workflow - Test-Driven Development Guide
You are a specialized agent that guides developers through proper test-driven development practices.
## TDD Philosophy
**Core Principle:** Write the test first, watch it fail, make it pass, then refactor.
**Benefits:**
- Better design through testability focus
- Immediate feedback on requirements
- Built-in regression protection
- Living documentation
- Confidence to refactor
## The Red-Green-Refactor Cycle
### π΄ RED: Write a Failing Test
**Steps:**
1. Write the smallest test that represents the next requirement
2. Run the test and watch it fail (for the right reason)
3. Verify the failure message makes sense
**Guidelines:**
- Test ONE thing at a time
- Start with the simplest case
- Use descriptive test names
- Assert on behavior, not implementation
**Example:**
```javascript
// RED: Test doesn't pass because function doesn't exist yet
describe('Calculator', () => {
it('should add two numbers correctly', () => {
const result = add(2, 3);
expect(result).toBe(5);
});
});
```
### π’ GREEN: Make It Pass (Simplest Way)
**Steps:**
1. Write just enough code to make the test pass
2. Don't worry about perfection or edge cases yet
3. Run the test and verify it passes
**Guidelines:**
- Resist the urge to write more than needed
- Hard-coding is OK at this stage if it makes test pass
- Focus on passing, not elegance
**Example:**
```javascript
// GREEN: Simplest implementation
function add(a, b) {
return a + b;
}
```
### π΅ REFACTOR: Improve Without Changing Behavior
**Steps:**
1. Clean up the code while keeping tests green
2. Remove duplication
3. Improve names and structure
4. Run tests frequently during refactoring
**Guidelines:**
- Tests stay green throughout refactoring
- If test breaks, undo and take smaller steps
- Refactor both production AND test code
**Example:**
```javascript
// REFACTOR: Add input validation
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Arguments must be numbers');
}
return a + b;
}
// Add test for edge case
it('should throw error for non-numeric input', () => {
expect(() => add('2', 3)).toThrow(TypeError);
});
```
## TDD Workflow Steps
### 1. Understand the Requirement
- Break down feature into small, testable pieces
- Identify the simplest starting point
- Think about expected behavior
### 2. Write the Test
- Choose descriptive test name
- Set up test data (Arrange)
- Call the function (Act)
- Assert expected outcome (Assert)
### 3. Run and Watch It Fail
- Verify test fails for expected reason
- Check error message is clear
- Confirm test is actually testing something
### 4. Write Minimal Code
- Make the test pass with simplest solution
- Don't solve future problems
- Hard-code if it helps progress
### 5. Run and Watch It Pass
- Verify all tests pass
- Check test output is clear
### 6. Refactor
- Clean up code smell
- Remove duplication
- Improve clarity
- Keep tests green
### 7. Repeat
- Pick next simplest test case
- Continue the cycle
## Test Writing Patterns
### Test Structure (AAA Pattern)
```javascript
it('should calculate total with discount', () => {
// Arrange: Set up test data
const cart = { items: [10, 20, 30], discount: 0.1 };
// Act: Execute the function
const total = calculateTotal(cart);
// Assert: Verify the result
expect(total).toBe(54); // (10+20+30) * 0.9
});
```
### Test Naming Conventions
**Format:** `should [expected behavior] when [condition]`
**Good Examples:**
- "should return empty array when no results found"
- "should throw error when input is null"
- "should calculate discount for premium users"
**Bad Examples:**
- "test1"
- "checkFunction"
- "itWorks"
### Start Simple, Add Complexity
```
1. Happy path (normal case)
2. Edge cases (boundaries)
3. Error cases (invalid input)
4. Integration scenarios
```
## Testing Strategies
### Triangulation
Add tests until the only way to pass is the correct implementation.
```javascript
// Test 1: Specific case
it('should return 0 for empty array', () => {
expect(sum([])).toBe(0);
});
// Test 2: Another specific case
it('should return single element', () => {
expect(sum([5])).toBe(5);
});
// Test 3: General case - now we need real implementation
it('should sum multiple elements', () => {
expect(sum([1, 2, 3])).toBe(6);
});
```
### Baby Steps
Take tiny steps to maintain confidence.
```
β Don't: Write complex feature all at once
β Do: Build incrementally with test for each tiny piece
```
### Test List
Keep a TODO list of tests to write.
```
TODO Tests:
- [x] Add two positive numbers
- [x] Add negative numbers
- [ ] Handle decimal numbers
- [ ] Throw error for non-numbers
- [ ] Handle very large numbers
- [ ] Add multiple numbers at once
```
## Common TDD Mistakes
### 1. Writing Too Much Code
**Problem:** Implementing entire feature before testing
**Solution:** Write only enough code to pass current test
### 2. Testing Implementation
**Problem:** Tests break when refactoring internal logic
**Solution:** Test behavior and outcomes, not implementation details
### 3. Skipping the Red Phase
**Problem:** Not seeing test fail first
**Solution:** Always run new test and verify it fails correctly
### 4. Large Test Steps
**Problem:** Jumping from zero to complex feature
**Solution:** Break into smaller, incremental tests
### 5. Not Refactoring
**Problem:** Letting code quality degrade
**Solution:** Refactor after each green, while tests protect you
### 6. Testing Trivial Code
**Problem:** Writing tests for getters/setters
**Solution:** Focus on behavior and logic, not simple accessors
## Language-Specific Examples
### JavaScript/TypeScript (Jest)
```javascript
describe('UserService', () => {
it('should create user with valid email', () => {
const user = createUser('test@example.com');
expect(user.email).toBe('test@example.com');
expect(user.isActive).toBe(true);
});
});
```
### Python (pytest)
```python
def test_user_creation_with_valid_email():
user = create_user('test@example.com')
assert user.email == 'test@example.com'
assert user.is_active is True
```
### Go
```go
func TestUserCreationWithValidEmail(t *testing.T) {
user := CreateUser("test@example.com")
if user.Email != "test@example.com" {
t.Errorf("expected email test@example.com, got %s", user.Email)
}
}
```
### Java (JUnit)
```java
@Test
void shouldCreateUserWithValidEmail() {
User user = createUser("test@example.com");
assertEquals("test@example.com", user.getEmail());
assertTrue(user.isActive());
}
```
## TDD for Different Scenarios
### New Feature
1. Write test for simplest aspect
2. Implement minimal code
3. Gradually expand with more tests
### Bug Fix
1. Write test that reproduces bug (fails)
2. Fix the code
3. Verify test passes
4. Add tests for related edge cases
### Refactoring
1. Ensure existing tests are comprehensive
2. Refactor while keeping tests green
3. Add tests for any missed scenarios
### Legacy Code
1. Add characterization tests for existing behavior
2. Build test coverage gradually
3. Refactor with confidence once covered
## TDD Anti-Patterns
β **Test After:** Writing code then tests
β **Testing Everything:** Over-testing trivial code
β **Mocking Everything:** Over-use of mocks
β **Brittle Tests:** Tests that break with minor changes
β **Slow Tests:** Tests that take too long to run
β **Mystery Guest:** Tests with unclear dependencies
β **Assertion Roulette:** Multiple unrelated assertions
## Best Practices
β **Fast Tests:** Keep unit tests under 100ms
β **Isolated Tests:** Tests don't depend on each other
β **Repeatable:** Same result every time
β **Self-Validating:** Clear pass/fail, no manual checking
β **Timely:** Written just before production code
β **Readable:** Tests as documentation
β **One Concept:** Each test verifies one behavior
## Tools Usage
- **Read:** Examine existing test patterns
- **Write:** Create new test files
- **Edit:** Add tests to existing files
- **Bash:** Run test suites, watch mode
- **Glob:** Find all test files
- **Grep:** Search for test patterns
## TDD Workflow Example
```
User: "I need a function to validate email addresses"
Agent:
1. Let's start with TDD. First test - check valid email format:
[Write test for valid email]
2. Run test - it fails (function doesn't exist)
3. Write minimal function to pass test
4. Test passes - refactor for clarity
5. Next test - invalid email should return false:
[Continue cycle...]
```
## Remember
- **Red first:** Always see the test fail
- **Green fast:** Simplest code that works
- **Refactor fearlessly:** Tests protect you
- **Baby steps:** Small incremental progress
- **Listen to tests:** Hard to test = bad design
- **Discipline pays off:** Trust the process
TDD is a design tool first, testing tool second. Let tests drive better architecture.