npx claudepluginhub duthaho/claudekit --plugin claudekitThis skill uses the workspace's default tool permissions.
- New feature development
Enforces strict TDD: write minimal failing test first for features/bugfixes/refactors, verify failure, add minimal passing code, refactor while green. Prevents untested code.
Enforces strict Test-Driven Development (TDD): write failing test first for features, bug fixes, refactors before any production code.
Enforces strict Test-Driven Development for features, bugfixes, refactors: write failing test first, verify fail, minimal code to pass, refactor. Red-Green-Refactor cycle.
Share bugs, ideas, or general feedback.
Write a minimal test demonstrating the desired behavior:
describe('calculateTotal', () => {
it('should sum item prices', () => {
const items = [{ price: 10 }, { price: 20 }];
expect(calculateTotal(items)).toBe(30);
});
});
Python equivalent:
# tests/test_cart.py
def test_calculate_total_sums_item_prices():
items = [{"price": 10}, {"price": 20}]
assert calculate_total(items) == 30
Run the test and confirm it fails for the right reason:
# TypeScript
npm test -- --grep "sum item prices"
# Expected: FAIL — calculateTotal is not defined
# Python
pytest tests/test_cart.py -v
# Expected: FAIL — NameError: name 'calculate_total' is not defined
Critical: The failure should be because the feature doesn't exist, not because of typos or syntax errors.
Write the simplest code that makes the test pass:
function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}
# src/services/cart.py
def calculate_total(items: list[dict]) -> int:
return sum(item["price"] for item in items)
Don't over-engineer. If the test passes with simple code, stop.
Run the test and confirm it passes:
# TypeScript
npm test -- --grep "sum item prices"
# Expected: PASS
# Python
pytest tests/test_cart.py -v
# Expected: PASS
With green tests, refactor safely:
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
This is not a guideline. It's a rule.
Delete it. Completely.
WRONG: "I'll keep this code as reference while writing tests"
RIGHT: Delete the code, write test, rewrite implementation
// BAD: Multiple behaviors
it('should validate and save user', () => {
expect(validateUser(user)).toBe(true);
expect(saveUser(user)).toBe(1);
});
// GOOD: Single behavior
it('should validate user email format', () => {
expect(validateUser({ email: 'test@example.com' })).toBe(true);
});
it('should save valid user', () => {
const user = createValidUser();
expect(saveUser(user)).toBe(1);
});
Test names should describe the behavior:
// BAD
it('test1', () => {});
it('calculateTotal', () => {});
// GOOD
it('should return 0 for empty cart', () => {});
it('should apply discount when coupon is valid', () => {});
Use real implementations when possible:
// PREFER: Real database (test container)
const db = await startTestDatabase();
const result = await userRepo.save(user);
// AVOID: Excessive mocking
const mockDb = { save: jest.fn().mockResolvedValue(1) };
# PREFER: Real database (test fixture)
@pytest.fixture
async def db_session(async_engine):
async with AsyncSession(async_engine) as session:
yield session
async def test_save_user(db_session):
user = User(email="test@example.com", name="Test")
db_session.add(user)
await db_session.commit()
assert user.id is not None
# AVOID: Excessive mocking
def test_save_user_mocked():
mock_db = MagicMock()
mock_db.add.return_value = None # proves nothing
Test what the code does, not how it does it:
// BAD: Testing implementation
it('should call helper function', () => {
calculateTotal(items);
expect(helperFn).toHaveBeenCalled();
});
// GOOD: Testing behavior
it('should return correct total', () => {
expect(calculateTotal(items)).toBe(30);
});
Tests written after code verify what was written, not what should happen. The test can't prove the code is correct if it was shaped to match existing code.
Ad-hoc testing is not systematic. It misses edge cases, isn't repeatable, and doesn't prevent regressions.
Simple code breaks too. A test takes seconds and provides permanent verification.
TDD is faster in the medium term. Debugging time saved far exceeds test-writing time.
Sunk cost fallacy. Delete and rewrite properly.
Always include tests for:
describe('calculateTotal', () => {
it('should return 0 for empty array', () => {
expect(calculateTotal([])).toBe(0);
});
it('should handle null items array', () => {
expect(() => calculateTotal(null)).toThrow();
});
it('should handle negative prices', () => {
const items = [{ price: -10 }, { price: 20 }];
expect(calculateTotal(items)).toBe(10);
});
});
def test_calculate_total_empty_list():
assert calculate_total([]) == 0
def test_calculate_total_none_raises():
with pytest.raises(TypeError):
calculate_total(None)
def test_calculate_total_negative_prices():
items = [{"price": -10}, {"price": 20}]
assert calculate_total(items) == 10
Write the test with httpx.AsyncClient first, then implement the route:
# 1. RED — test first
import pytest
from httpx import AsyncClient
@pytest.mark.anyio
async def test_create_order_returns_201(client: AsyncClient):
response = await client.post("/api/orders", json={"item": "widget", "quantity": 2})
assert response.status_code == 201
assert response.json()["item"] == "widget"
# 2. GREEN — implement route
from fastapi import APIRouter, status
from pydantic import BaseModel
router = APIRouter(prefix="/api/orders")
class CreateOrderRequest(BaseModel):
item: str
quantity: int
@router.post("", status_code=status.HTTP_201_CREATED)
async def create_order(body: CreateOrderRequest):
return {"id": "ord_1", "item": body.item, "quantity": body.quantity}
Write the test with supertest first, then implement the controller:
// 1. RED — test first
it('POST /orders — creates order', () =>
request(app.getHttpServer())
.post('/orders')
.send({ item: 'widget', quantity: 2 })
.expect(201)
.expect((res) => {
expect(res.body.item).toBe('widget');
}));
// 2. GREEN — implement controller
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() dto: CreateOrderDto) {
return this.ordersService.create(dto);
}
Write the test with Testing Library first, then implement the component:
// 1. RED — test first
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
it('should call onSubmit with form data', async () => {
const onSubmit = vi.fn();
render(<OrderForm onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText('Item'), 'widget');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(onSubmit).toHaveBeenCalledWith(expect.objectContaining({ item: 'widget' }));
});
// 2. GREEN — implement component
export function OrderForm({ onSubmit }: { onSubmit: (data: OrderData) => void }) {
// minimal implementation to pass the test
}
The methodology catches bugs before commit:
This is faster than:
verification-before-completion -- Ensures tests are actually run and passing before claiming work is donetesting-anti-patterns -- Avoid common testing mistakes that undermine TDD effectivenesspytest -- Python-specific testing patterns and best practices for TDDvitest -- TypeScript/JavaScript-specific testing patterns and best practices for TDDwriting-plans — Planning implementation tasks for TDD workflow