From dev-infrastructure-skills
This skill should be used when the user asks to "write tests first", "do TDD", "test-driven development", "write unit tests", "add test coverage", "create a test suite", "follow red-green-refactor", or needs guidance on testing workflows, test structure, mocking strategies, assertion patterns, or building confidence in code through automated testing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dev-infrastructure-skills:test-driven-developmentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The Iron Law of TDD: **NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.**
The Iron Law of TDD: NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
TDD is not "write code then add tests." It is a design discipline where tests drive the implementation. Every line of production code exists because a test required it.
Write the smallest test that expresses a new behavior. Run it. It MUST fail. If it passes, either the test is wrong or the behavior already exists.
// RED: Test for a function that doesn't exist yet
describe('calculateDiscount', () => {
it('applies 10% discount for orders over $100', () => {
expect(calculateDiscount(150)).toBe(135);
});
});
Write the simplest, most direct code that makes the failing test pass. No more. No less. Do not anticipate future needs.
// GREEN: Simplest code that passes
function calculateDiscount(amount: number): number {
return amount * 0.9;
}
Clean up the code while keeping all tests green. Extract functions, rename variables, remove duplication. Tests are your safety net.
// REFACTOR: Handle edge cases revealed by next tests
function calculateDiscount(amount: number): number {
const DISCOUNT_THRESHOLD = 100;
const DISCOUNT_RATE = 0.10;
if (amount <= DISCOUNT_THRESHOLD) return amount;
return amount * (1 - DISCOUNT_RATE);
}
Tests should describe WHAT the code does, not HOW it does it internally.
// BAD: Tests implementation details
it('calls the internal _calculate method', () => {
const spy = jest.spyOn(cart, '_calculate');
cart.getTotal();
expect(spy).toHaveBeenCalled();
});
// GOOD: Tests observable behavior
it('returns the sum of all item prices', () => {
cart.addItem({ price: 10 });
cart.addItem({ price: 20 });
expect(cart.getTotal()).toBe(30);
});
Each test should verify one logical concept. Multiple assertions are fine if they all verify the same behavior.
// GOOD: Multiple assertions, one concept (user creation)
it('creates a user with default settings', () => {
const user = createUser({ name: 'Alice' });
expect(user.name).toBe('Alice');
expect(user.role).toBe('member');
expect(user.active).toBe(true);
});
Every test follows three clear sections:
it('calculates total with tax', () => {
// Arrange
const cart = new Cart();
cart.addItem({ price: 100 });
// Act
const total = cart.getTotalWithTax(0.08);
// Assert
expect(total).toBe(108);
});
Test names should read as specifications. Someone reading only test names should understand the system's behavior.
// BAD
it('works correctly', () => {});
it('test 1', () => {});
// GOOD
it('rejects passwords shorter than 8 characters', () => {});
it('sends welcome email after successful registration', () => {});
it('returns 404 when item does not exist', () => {});
| Rationalization | Reality |
|---|---|
| "I'll add tests later" | Later never comes. Untested code accumulates. |
| "This is too simple to test" | Simple code gets complex. Tests document intent. |
| "Tests slow me down" | Tests accelerate you after the first 30 minutes. Debugging without tests is slower. |
| "I'll just test the important parts" | You can't know what's important until something breaks. |
| "The deadline is too tight" | Bugs found in production cost 10x more than tests written now. |
Test a single function or class. Mock external dependencies.
describe('PriceCalculator', () => {
it('applies bulk discount at 10+ items', () => {
const calc = new PriceCalculator();
expect(calc.calculate({ quantity: 10, unitPrice: 50 })).toBe(450);
});
});
Test how multiple units work together. Use real dependencies where practical.
describe('OrderService', () => {
it('creates order and updates inventory', async () => {
const order = await orderService.place({ itemId: '1', quantity: 2 });
expect(order.status).toBe('confirmed');
const item = await inventoryService.getItem('1');
expect(item.stock).toBe(8); // was 10
});
});
Test complete user workflows. Use Playwright or similar. See the playwright-testing skill for patterns.
Before committing, verify:
npx claudepluginhub moxywolfllc/moxywolf-plugins --plugin dev-infrastructure-skillsCreates bite-sized, testable implementation plans from specs or requirements, with file structure and task decomposition. Activates before coding multi-step tasks.