Skip to main content
test-driven-development RED-GREEN-REFACTOR — write a failing test, make it pass, refactor. Use when writing production code, or on mention of TDD or test-first.
Ir a la instalación Skills Marketplace Descubre y explora habilidades de IA creadas por la comunidad.
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Copiar promptMostrar detalles del prompt Un comando directo omite el prompt de revisión. Revisa el origen antes de ejecutarlo.
npx skills add https://github.com/MadAppGang/magus --skill test-driven-developmentEl comando permanece en una sola línea. Desplázate horizontalmente para revisarlo antes de copiarlo.
¿Prefieres una copia local? Descarga los archivos que SkillsMP tiene disponibles ahora.
Descargar Zip Descargando... Más de este repositorio Picks the browser-use agent brain LLM per task. Use when choosing a model for retry_with_browser_use_agent or a cloud session, or the user mentions a cheap, slow, or smart model.
Reference for the Browser Use MCP tools — parameters, returns, session lifecycle. Use when navigating, clicking, typing, evaluating JS, or extracting page content.
Visual UI debugging — screenshot capture and analysis, responsive layout checking at multiple viewport sizes, CSS validation via DOM state, visual regression detection, before/after state comparison.
SOC
Basado en la clasificación ocupacional SOC
name test-driven-development description RED-GREEN-REFACTOR — write a failing test, make it pass, refactor. Use when writing production code, or on mention of TDD or test-first. user-invocable false
Test-Driven Development (TDD)
Iron Law: "NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST"
When to Use
Use this skill when:
Writing new functions, methods, or classes
Adding features to existing code
Fixing bugs (write test that reproduces bug first)
Refactoring code (tests verify behavior preservation)
Implementing API endpoints or business logic
Creating UI components with testable behavior
Red Flags (Violation Indicators)
Watch for these patterns that indicate TDD violations:
No Test for Changes - Modifying code without adding corresponding test ("Just a small change, doesn't need a test")
"Just a Small Fix" - Bypassing TDD for "quick fixes" ("It's only one line, I don't need a test")Violation Detection: If you find yourself saying "I'll test this after I get it working," you're violating TDD.
RED-GREEN-REFACTOR Cycle TDD follows a strict 3-phase workflow:
Phase 1: RED (Write a Failing Test) Objective: Specify desired behavior through a test that fails
Write the test first - Before any production code exists
Define expected behavior - What should the code do?
Use API you wish existed - Test the interface you want
Run the test - Verify it fails (if it passes, you didn't test new behavior)
Check failure reason - Ensure it fails for the right reason (missing code, not syntax error)
Example (TypeScript/Jest):
describe ('UserValidator' , () => {
it ('rejects email without @ symbol' , () => {
const validator = new UserValidator ();
const result = validator.validateEmail ('invalid-email' );
expect (result.isValid ).toBe (false );
expect (result.error ).toBe ('Email must contain @ symbol' );
});
});
Red Phase Complete When: Test fails with expected error message
Phase 2: GREEN (Make the Test Pass) Objective: Write minimal code to make test pass
Write minimal code - Just enough to pass the test
Don't optimize yet - Resist the urge to add "nice-to-have" features
Run the test - Verify it passes
Commit frequently - Small, passing test = commit point
interface ValidationResult {
isValid : boolean ;
error ?: string ;
}
export class UserValidator {
validateEmail (email : string ): ValidationResult {
if (!email.includes ('@' )) {
return { isValid : false , error : 'Email must contain @ symbol' };
}
return { isValid : true };
}
}
Green Phase Complete When: Test passes consistently
Phase 3: REFACTOR (Improve Design) Objective: Improve code quality while keeping tests green
Look for duplication - Extract repeated code
Improve naming - Make intent clearer
Simplify logic - Reduce complexity
Run tests after each change - Ensure behavior preserved
Commit when satisfied - Refactored code + passing tests = commit
Example (TypeScript - Refactored):
interface ValidationResult {
isValid : boolean ;
error ?: string ;
}
export class UserValidator {
private static readonly EMAIL_REQUIRED_CHARS = '@' ;
private static readonly EMAIL_ERROR = 'Email must contain @ symbol' ;
validateEmail (email : string ): ValidationResult {
if (!this .containsRequiredChars (email)) {
return this .createError (UserValidator .EMAIL_ERROR );
}
return this .createSuccess ();
}
private containsRequiredChars (email : string ): boolean {
return email.includes (UserValidator .EMAIL_REQUIRED_CHARS );
}
private createError (message : string ): ValidationResult {
return { isValid : false , error : message };
}
private createSuccess (): ValidationResult {
return { isValid : true };
}
}
Refactor Phase Complete When: Code is clean AND tests still pass
Anti-patterns Table Anti-pattern ✗ Wrong Approach ✓ Correct TDD Approach Test-After Write calculateTotal() function, then write tests Write test for calculateTotal(), see it fail, implement function Empty Tests Write test that always passes: expect(true).toBe(true) Write test that fails until production code is correct Happy-Path-Only Test only valid inputs: validateEmail('user@example.com') Test invalid inputs too: validateEmail('no-at-sign'), validateEmail('') Skip-Simple Skip test for "obvious" add(a, b) { return a + b } Write test: expect(add(2, 3)).toBe(5) - bugs hide in "simple" code Change-Then-Test Modify calculateDiscount(), run app manually, then add test Write failing test showing bug, modify code until test passes
Testing Strategy by Code Type
Pure Functions Pattern: 1 happy path + 3 edge cases minimum
describe ('calculateDiscount' , () => {
it ('applies 10% discount to $100 purchase' , () => {
expect (calculateDiscount (100 , 0.1 )).toBe (90 );
});
it ('returns 0 for negative amounts' , () => {
expect (calculateDiscount (-50 , 0.1 )).toBe (0 );
});
it ('returns original amount for 0 discount' , () => {
expect (calculateDiscount (100 , 0 )).toBe (100 );
});
it ('throws error for discount > 1' , () => {
expect (() => calculateDiscount (100 , 1.5 )).toThrow ('Discount must be <= 1' );
});
});
Error Handlers Pattern: 1 test per error type + 1 success case
def test_divide_by_zero_raises_error ():
with pytest.raises(ZeroDivisionError, match ="Cannot divide by zero" ):
divide(10 , 0 )
def test_divide_non_numeric_raises_error ():
with pytest.raises(TypeError, match ="Arguments must be numbers" ):
divide("10" , 5 )
def test_divide_returns_float ():
result = divide(10 , 3 )
assert result == pytest.approx(3.333 , rel=1e-3 )
UI Components Pattern: 1 render test + 3 interaction tests
Example (TypeScript/React Testing Library):
describe ('LoginForm' , () => {
it ('renders email and password inputs' , () => {
render (<LoginForm /> );
expect (screen.getByLabelText ('Email' )).toBeInTheDocument ();
expect (screen.getByLabelText ('Password' )).toBeInTheDocument ();
});
it ('shows error for invalid email' , async () => {
render (<LoginForm /> );
await userEvent.type (screen.getByLabelText ('Email' ), 'invalid' );
await userEvent.click (screen.getByRole ('button' , { name : 'Login' }));
expect (screen.getByText ('Invalid email address' )).toBeInTheDocument ();
});
it ('disables submit button while loading' , async () => {
render (<LoginForm onSubmit ={async () => await delay(1000)} /> );
const submitButton = screen.getByRole ('button' , { name : 'Login' });
await userEvent.click (submitButton);
expect (submitButton).toBeDisabled ();
});
it ('calls onSubmit with form data' , async () => {
const onSubmit = jest.fn ();
render (<LoginForm onSubmit ={onSubmit} /> );
await userEvent.type (screen.getByLabelText ('Email' ), 'user@example.com' );
await userEvent.type (screen.getByLabelText ('Password' ), 'password123' );
await userEvent.click (screen.getByRole ('button' , { name : 'Login' }));
expect (onSubmit).toHaveBeenCalledWith ({
email : 'user@example.com' ,
password : 'password123'
});
});
});
API Endpoints Pattern: 1 success test + 2 error tests
func TestGetUser (t *testing.T) {
t.Run("returns user for valid ID" , func (t *testing.T) {
req := httptest.NewRequest("GET" , "/users/123" , nil )
w := httptest.NewRecorder()
handler := NewUserHandler(mockUserRepo)
handler.GetUser(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.Contains(t, w.Body.String(), `"id":"123"` )
})
t.Run("returns 404 for non-existent user" , func (t *testing.T) {
req := httptest.NewRequest("GET" , "/users/999" , nil )
w := httptest.NewRecorder()
handler := NewUserHandler(mockUserRepo)
handler.GetUser(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
})
t.Run("returns 400 for invalid ID format" , func (t *testing.T) {
req := httptest.NewRequest("GET" , "/users/invalid" , nil )
w := httptest.NewRecorder()
handler := NewUserHandler(mockUserRepo)
handler.GetUser(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
Examples
Example 1: Bug Fix with TDD (TypeScript) Scenario: User reports: "App crashes when searching with empty query"
Step 1 (RED): Write test that reproduces bug
describe ('searchProducts' , () => {
it ('returns empty array for empty query' , () => {
const result = searchProducts ('' );
expect (result).toEqual ([]);
});
});
Step 2 (GREEN): Fix the bug
export function searchProducts (query : string ): Product [] {
if (!query || query.trim () === '' ) {
return [];
}
return products.filter (p => p.name .toLowerCase ().includes (query.toLowerCase ()));
}
Step 3 (REFACTOR): Add more edge cases
describe ('searchProducts' , () => {
it ('returns empty array for empty query' , () => {
expect (searchProducts ('' )).toEqual ([]);
});
it ('returns empty array for whitespace-only query' , () => {
expect (searchProducts (' ' )).toEqual ([]);
});
it ('is case-insensitive' , () => {
expect (searchProducts ('LAPTOP' )).toHaveLength (3 );
});
});
Example 2: New Feature with TDD (Python) Scenario: Add email validation to user registration
Step 1 (RED): Write test for feature that doesn't exist
def test_user_creation_validates_email ():
with pytest.raises(ValidationError, match ="Invalid email format" ):
User.create(email="not-an-email" , password="secret123" )
Step 2 (GREEN): Implement validation
import re
class ValidationError (Exception ):
pass
class User :
@classmethod
def create (cls, email: str , password: str ):
if not re.match (r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' , email):
raise ValidationError("Invalid email format" )
return cls(email, password)
Step 3 (REFACTOR): Extract validation logic
class EmailValidator :
PATTERN = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
@classmethod
def validate (cls, email: str ) -> bool :
return bool (re.match (cls.PATTERN, email))
class User :
@classmethod
def create (cls, email: str , password: str ):
if not EmailValidator.validate(email):
raise ValidationError("Invalid email format" )
return cls(email, password)
Example 3: API Development with TDD (Go) Scenario: Implement POST /api/orders endpoint
Step 1 (RED): Write test for non-existent endpoint
func TestCreateOrder (t *testing.T) {
t.Run("creates order with valid data" , func (t *testing.T) {
payload := `{"user_id": "123", "items": [{"product_id": "456", "quantity": 2}]}`
req := httptest.NewRequest("POST" , "/api/orders" , strings.NewReader(payload))
w := httptest.NewRecorder()
handler := NewOrderHandler(mockOrderRepo)
handler.CreateOrder(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
assert.Contains(t, w.Body.String(), `"order_id"` )
})
}
Step 2 (GREEN): Implement handler
func (h *OrderHandler) CreateOrder(w http.ResponseWriter, r *http.Request) {
var req CreateOrderRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request" , http.StatusBadRequest)
return
}
order, err := h.orderRepo.Create(req.UserID, req.Items)
if err != nil {
http.Error(w, "Failed to create order" , http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(map [string ]string {"order_id" : order.ID})
}
Step 3 (REFACTOR): Add error handling tests
func TestCreateOrder (t *testing.T) {
t.Run("creates order with valid data" , func (t *testing.T) {
})
t.Run("returns 400 for invalid JSON" , func (t *testing.T) {
req := httptest.NewRequest("POST" , "/api/orders" , strings.NewReader("invalid" ))
w := httptest.NewRecorder()
handler.CreateOrder(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
t.Run("returns 400 for missing user_id" , func (t *testing.T) {
payload := `{"items": [{"product_id": "456", "quantity": 2}]}`
req := httptest.NewRequest("POST" , "/api/orders" , strings.NewReader(payload))
w := httptest.NewRecorder()
handler.CreateOrder(w, req)
assert.Equal(t, http.StatusBadRequest, w.Code)
})
}
Integration This skill integrates with:
verification-before-completion : Test output is primary evidence for completion claims
systematic-debugging : When test fails, use debugging workflow to find root cause
code-review : Tests serve as executable documentation and specification
refactoring : Tests enable safe refactoring (verify behavior preservation)
Common Objections & Responses Objection: "TDD is too slow, I can code faster without tests"
Response: Writing tests first actually saves time by catching bugs early. Debugging later is far more expensive than preventing bugs upfront.
Objection: "This code is too simple to test"
Response: Simple code is fastest to test. If it's truly simple, the test takes 30 seconds. If you can't write a fast test, the code isn't simple.
Objection: "I'll write tests after I figure out the design"
Response: Tests ARE the design. Writing tests first forces you to think about API usability before implementation locks you in.
Objection: "I need to see if my approach works before committing to tests"
Response: That's what the RED phase is for - write a test describing your desired approach, then implement it. If approach changes, update test first.
Summary Core Principle: Tests first, code second, refactor third.
RED: Write failing test
GREEN: Make test pass
REFACTOR: Improve design
Catches bugs before they ship
Documents expected behavior
Enables fearless refactoring
Forces good API design
Remember: If you wrote production code without a failing test first, you violated TDD. Delete the code and start over with a test.