skills/testing/test-driven-development/SKILL.md
Use when adding features or fixing bugs. Follow RED/GREEN/REFACTOR cycle. Write failing test first, implement minimal code to pass, then refactor.
npx skillsauth add liauw-media/codeassist test-driven-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Write tests BEFORE writing implementation code. Follow the RED → GREEN → REFACTOR cycle.
TEST FIRST, CODE SECOND.
Writing tests after code is NOT TDD. It's test-after-development (TAD) and misses TDD's benefits.
Benefits: ✅ Catches bugs early (before they exist) ✅ Forces you to think through design ✅ Creates better APIs (you're the first user) ✅ Provides safety net for refactoring ✅ Documentation through examples ✅ Higher confidence in changes
Without TDD: ❌ "It works on my machine" syndrome ❌ Fear of refactoring (might break things) ❌ Bugs discovered in production ❌ Unclear requirements until you code ❌ No safety net
Why it must fail first:
Why minimal:
Why separate refactoring:
I'm using the test-driven-development skill to implement this feature.
Following RED → GREEN → REFACTOR cycle.
🔴 RED Phase: Writing failing test
Test: User registration returns 201 and token
test/Feature/AuthenticationTest.php:
```php
public function test_user_can_register_with_valid_data()
{
$response = $this->postJson('/api/register', [
'name' => 'John Doe',
'email' => '[email protected]',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertStatus(201)
->assertJsonStructure(['token', 'user']);
$this->assertDatabaseHas('users', [
'email' => '[email protected]'
]);
}
Running test (with database backup):
./scripts/safe-test.sh vendor/bin/paratest --filter=test_user_can_register
Result: ❌ FAILED Error: Route [/api/register] not defined
✅ Test fails for the right reason (feature doesn't exist yet)
### Step 3: GREEN - Make Test Pass
🟢 GREEN Phase: Minimal implementation
Step 1: Create route routes/api.php:
Route::post('/register', [AuthController::class, 'register']);
Step 2: Create controller method app/Http/Controllers/AuthController.php:
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'user' => $user,
'token' => $token
], 201);
}
Running test again:
./scripts/safe-test.sh vendor/bin/paratest --filter=test_user_can_register
Result: ✅ PASSED
🎉 Test is GREEN!
### Step 4: REFACTOR - Improve Code
🔵 REFACTOR Phase: Improving code
Issues identified:
Refactoring:
public function generateAuthToken(): string
{
return $this->createToken('auth_token')->plainTextToken;
}
public function register(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users',
'password' => 'required|min:8|confirmed',
]);
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => Hash::make($validated['password']),
]);
return response()->json([
'user' => $user,
'token' => $user->generateAuthToken()
], 201);
}
Running test after refactor:
./scripts/safe-test.sh vendor/bin/paratest --filter=test_user_can_register
Result: ✅ STILL PASSED
✅ Refactoring successful (tests still green)
### Step 5: Repeat for Next Test
Moving to next test case...
🔴 RED: Registration with invalid email fails [Write failing test]
🟢 GREEN: Add validation [Make test pass]
🔵 REFACTOR: Extract validation to FormRequest [Improve code while keeping tests green]
Continue cycle...
## TDD Examples by Feature Type
### Example 1: New API Endpoint
Feature: User login endpoint
🔴 RED: test/Feature/AuthenticationTest.php:
public function test_user_can_login_with_correct_credentials()
{
$user = User::factory()->create([
'email' => '[email protected]',
'password' => Hash::make('password123')
]);
$response = $this->postJson('/api/login', [
'email' => '[email protected]',
'password' => 'password123',
]);
$response->assertStatus(200)
->assertJsonStructure(['token']);
}
Run: ❌ FAILS (route doesn't exist)
🟢 GREEN:
Run: ✅ PASSES
🔵 REFACTOR:
Run: ✅ STILL PASSES
### Example 2: Bug Fix
Bug: User can login with wrong password
🔴 RED: Write test that exposes the bug
public function test_user_cannot_login_with_wrong_password()
{
$user = User::factory()->create([
'email' => '[email protected]',
'password' => Hash::make('correct_password')
]);
$response = $this->postJson('/api/login', [
'email' => '[email protected]',
'password' => 'wrong_password',
]);
$response->assertStatus(401)
->assertJson(['message' => 'Invalid credentials']);
}
Run: ❌ FAILS (bug exists - returns 200 instead of 401)
🟢 GREEN: Fix the bug
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (!Auth::attempt($credentials)) {
return response()->json([
'message' => 'Invalid credentials'
], 401);
}
$user = Auth::user();
$token = $user->generateAuthToken();
return response()->json(['token' => $token]);
}
Run: ✅ PASSES (bug fixed)
🔵 REFACTOR: Improve error handling Run: ✅ STILL PASSES
### Example 3: Refactoring Existing Code
Goal: Refactor User model to use traits
🔴 RED: Write tests for existing functionality FIRST
public function test_user_can_have_profile()
{
$user = User::factory()->create();
$profile = $user->profile()->create(['bio' => 'Hello']);
$this->assertEquals('Hello', $user->profile->bio);
}
public function test_user_can_have_posts()
{
$user = User::factory()->create();
$post = $user->posts()->create(['title' => 'First Post']);
$this->assertCount(1, $user->posts);
}
Run: ✅ PASSES (existing functionality works)
🟢 GREEN: Already green, skip to refactor
🔵 REFACTOR: Extract to traits
// app/Models/Traits/HasProfile.php
trait HasProfile
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
// app/Models/Traits/HasPosts.php
trait HasPosts
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// app/Models/User.php
class User extends Authenticatable
{
use HasProfile, HasPosts;
}
Run: ✅ STILL PASSES (refactor successful)
## TDD Best Practices
### Test Naming
**Good names:**
- `test_user_can_register_with_valid_data`
- `test_registration_fails_with_invalid_email`
- `test_authenticated_user_can_access_profile`
- `test_unauthenticated_user_receives_401`
**Bad names:**
- `test_auth` (too vague)
- `test_1` (meaningless)
- `testUserStuff` (unclear)
### Test Structure (AAA Pattern)
```php
public function test_example()
{
// Arrange: Set up test data
$user = User::factory()->create();
// Act: Perform the action
$response = $this->actingAs($user)->getJson('/api/profile');
// Assert: Verify the outcome
$response->assertStatus(200);
$this->assertEquals($user->id, $response['id']);
}
Each test should be completely independent:
// ✅ GOOD: Each test creates its own data
public function test_user_can_login()
{
$user = User::factory()->create(['password' => Hash::make('password')]);
// ... test login ...
}
public function test_user_can_logout()
{
$user = User::factory()->create();
// ... test logout ...
}
// ❌ BAD: Tests depend on order/shared state
class AuthTest extends TestCase
{
private $user;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create(); // Shared state
}
// These tests depend on setUp user
}
// ✅ GOOD: Tests one specific behavior
public function test_registration_requires_email()
{
$response = $this->postJson('/api/register', [
'name' => 'John',
// Missing email
'password' => 'password',
]);
$response->assertStatus(422)
->assertJsonValidationErrors('email');
}
// ❌ BAD: Tests multiple things
public function test_registration()
{
// Tests multiple validations, success case, database insertion
// If it fails, unclear which part failed
}
BAD: "Let me code this quickly, I'll add tests later"
Why bad: Miss TDD benefits, tests become less useful
Fix: Write test first, no exceptions
// ❌ BAD: Tests internal implementation
public function test_user_repository_uses_query_builder()
{
$repository = new UserRepository();
$this->assertInstanceOf(QueryBuilder::class, $repository->getBuilder());
}
Why bad: Breaks when refactoring internal code
Fix: Test behavior, not implementation
// ✅ GOOD: Tests behavior
public function test_can_find_user_by_email()
{
$user = User::factory()->create(['email' => '[email protected]']);
$found = $this->repository->findByEmail('[email protected]');
$this->assertEquals($user->id, $found->id);
}
BAD: Skip proper RED/GREEN cycle
Why bad: Might not test what you think
Fix: See test fail first, then make it pass
Use with:
executing-plans - Implement each task with TDDdatabase-backup - Before running testscode-review - Review tests and code togetherTDD helps:
brainstorming - Think through edge caseswriting-plans - Each task becomes a testrefactoring - Safe to refactor with testsFor each feature:
This skill is based on:
Social Proof: All major tech companies use TDD for critical systems.
When implementing features:
Bottom Line: TDD seems slower at first but is actually faster. Tests catch bugs immediately, not in production. Write tests first, always.
development
Use when decomposing complex work. Dispatch fresh subagent per task, review between tasks. Flow: Load plan → Dispatch task → Review output → Apply feedback → Mark complete → Next task. No skipping reviews, no parallel dispatch.
development
# Server Documentation System Set up a documentation system that tracks changes and maintains server/project documentation with Claude Code hooks. ## When to Use - Setting up a new server or development environment - Need to track configuration changes over time - Want automatic documentation of work sessions - Maintaining changelog for infrastructure ## Directory Structure ``` ~/docs/ # User home directory (cross-platform) ├── changelog.md # Global over
development
Delegate tasks to remote Claude Code agent containers for parallel execution, long-running analysis, or resource-intensive operations.
development
Use when working on multiple features simultaneously. Creates isolated workspaces without branch switching, enabling parallel development.