tdd-workflow-advanced
TDD anti-patterns — writing code before tests, testing implementation details instead of behavior, using waitForTimeout as a sync strategy, chaining tests that share state, mocking the system under test instead of its dependencies.
Best use case
tdd-workflow-advanced is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
TDD anti-patterns — writing code before tests, testing implementation details instead of behavior, using waitForTimeout as a sync strategy, chaining tests that share state, mocking the system under test instead of its dependencies.
Teams using tdd-workflow-advanced should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/tdd-workflow-advanced/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How tdd-workflow-advanced Compares
| Feature / Agent | tdd-workflow-advanced | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
TDD anti-patterns — writing code before tests, testing implementation details instead of behavior, using waitForTimeout as a sync strategy, chaining tests that share state, mocking the system under test instead of its dependencies.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# TDD Workflow — Anti-Patterns
This skill extends `tdd-workflow` with common testing mistakes and how to fix them. Load `tdd-workflow` first.
## When to Activate
- Tests were written after the implementation, not before
- Tests spy on private methods or internal state
- Tests use `waitForTimeout` / `sleep` as a synchronization mechanism
- Test B relies on test A having run first (shared state)
- The system under test itself is being mocked
---
## Anti-Patterns
### Writing Code Before Tests
**Wrong:**
```typescript
// implement first, then try to write tests around it
export function calculateDiscount(price: number, tier: string): number {
if (tier === 'gold') return price * 0.8
if (tier === 'silver') return price * 0.9
return price
}
// tests written after — they describe what the code happens to do, not what it should do
test('gold tier returns 80%', () => {
expect(calculateDiscount(100, 'gold')).toBe(80)
})
```
**Correct:**
```typescript
// write failing test first
test('gold tier gets 20% discount', () => {
expect(calculateDiscount(100, 'gold')).toBe(80) // RED: function does not exist yet
})
// then implement the minimum to make it pass
export function calculateDiscount(price: number, tier: string): number {
if (tier === 'gold') return price * 0.8
return price
}
```
**Why:** Code written before tests tends to be untestable by design; tests written after only verify what was already built, not what was required.
### Testing Implementation Details Instead of Behavior
**Wrong:**
```typescript
test('calls internal _normalize before saving', () => {
const spy = jest.spyOn(service as any, '_normalize')
service.save(user)
expect(spy).toHaveBeenCalled() // tightly coupled to private method name
})
```
**Correct:**
```typescript
test('saves user with normalized email', async () => {
await service.save({ ...user, email: ' User@EXAMPLE.COM ' })
const saved = await repo.findById(user.id)
expect(saved.email).toBe('user@example.com') // tests observable outcome
})
```
**Why:** Spying on private internals makes tests brittle to safe refactors; test observable inputs and outputs instead.
### Using `setTimeout` / `waitForTimeout` as a Synchronization Strategy
**Wrong:**
```typescript
test('search results appear after typing', async ({ page }) => {
await page.fill('input[name="search"]', 'election')
await page.waitForTimeout(600) // arbitrary debounce guess
await expect(page.locator('[data-testid="result"]')).toBeVisible()
})
```
**Correct:**
```typescript
test('search results appear after typing', async ({ page }) => {
await page.fill('input[name="search"]', 'election')
await expect(page.locator('[data-testid="result"]')).toBeVisible({ timeout: 5000 })
// Playwright retries the assertion until it passes or times out
})
```
**Why:** Fixed timeouts are both slow and fragile; use framework retries or explicit state-change assertions instead.
### Chaining Tests That Share State
**Wrong:**
```typescript
// test A creates the user
test('creates user', async () => {
await api.post('/users', { name: 'Alice' })
})
// test B depends on test A having run first
test('updates user', async () => {
await api.put('/users/alice', { name: 'Alice B.' })
const user = await api.get('/users/alice')
expect(user.name).toBe('Alice B.')
})
```
**Correct:**
```typescript
test('updates user', async () => {
// arrange: own setup, independent of other tests
await api.post('/users', { name: 'Alice' })
await api.put('/users/alice', { name: 'Alice B.' })
const user = await api.get('/users/alice')
expect(user.name).toBe('Alice B.')
})
```
**Why:** Tests that share state fail non-deterministically when run in isolation or in a different order.
### Mocking the System Under Test
**Wrong:**
```typescript
jest.mock('./userService') // mocks the thing we're actually testing
const { registerUser } = require('./userService')
registerUser.mockResolvedValue({ id: '1', name: 'Alice' })
test('registerUser returns a user', async () => {
const result = await registerUser('Alice', 'alice@example.com')
expect(result.name).toBe('Alice') // only tests the mock, not the real code
})
```
**Correct:**
```typescript
jest.mock('./userRepository') // mock the dependency, not the SUT
import { registerUser } from './userService'
import { userRepository } from './userRepository'
;(userRepository.save as jest.Mock).mockResolvedValue({ id: '1', name: 'Alice' })
test('registerUser persists and returns the new user', async () => {
const result = await registerUser('Alice', 'alice@example.com')
expect(result.name).toBe('Alice')
expect(userRepository.save).toHaveBeenCalledWith(expect.objectContaining({ name: 'Alice' }))
})
```
**Why:** Mocking the system under test bypasses all real logic; mock dependencies at the boundary, not the subject.
---
## Success Metrics
- 80%+ code coverage achieved
- All tests passing (green)
- No skipped or disabled tests
- Fast test execution (< 30s for unit tests)
- E2E tests cover critical user flows
- Tests catch bugs before production
**Remember**: Tests are not optional. They are the safety net that enables confident refactoring, rapid development, and production reliability.
## Reference
- `tdd-workflow` — Red-Green-Refactor cycle, test types, coverage targets, testing best practices
- `typescript-testing` — TypeScript-specific testing patterns and Jest/Vitest setup
- `e2e-testing` — Playwright E2E testing patternsRelated Skills
typescript-patterns-advanced
Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.
swift-patterns-advanced
Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.
slo-workflow
SLI/SLO/SLA and error budget workflow: define service level indicators, set objectives, calculate error budgets, implement burn rate alerting, and use error budgets to gate risky deployments. Covers Prometheus, Datadog, and Google SRE methodology.
serverless-patterns-advanced
Advanced Serverless patterns — Lambda idempotency (Lambda Powertools + DynamoDB persistence layer), Lambda cost model (pricing formula, break-even vs containers), and CloudWatch Insights observability queries for cold starts, duration, and errors.
security-review-advanced
Security anti-patterns — localStorage token storage (XSS risk), trusting client-side authorization checks, reflecting full error details to clients, blacklist vs whitelist input validation, using npm install instead of npm ci in CI pipelines.
rust-testing-advanced
Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with
rust-patterns-advanced
Advanced Rust patterns — zero-cost abstractions, proc macros, unsafe FFI, WASM, Axum web architecture, trait objects vs generics, and performance profiling.
python-testing-advanced
Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.
python-patterns-advanced
Advanced Python patterns — concurrency (threading, multiprocessing, async/await), hexagonal architecture with FastAPI, RFC 7807 error handling, memory optimization, pyproject.toml tooling, and anti-patterns. Extends python-patterns.
multi-agent-patterns-advanced
Advanced multi-agent patterns — capability registry, durable state (Redis/DynamoDB), task decomposition, testing multi-agent systems, pattern quick-selection guide, failure handling, cost management, and worktree isolation. Extends multi-agent-patterns.
microfrontend-patterns-advanced
Advanced Micro-Frontend patterns — testing strategy (unit per-remote, integration with mocked remotes, E2E full composition via Playwright), CI/CD independent deployments per remote, ErrorBoundary resilience, and monolith-to-MFE strangler-fig migration.
hexagonal-typescript-advanced
Advanced Hexagonal Architecture anti-patterns for TypeScript — domain importing framework dependencies, use cases depending on concrete adapters, HTTP handlers bypassing use cases, Zod validation inside the domain model. Each anti-pattern includes wrong/correct comparison with explanation.