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.

8 stars

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

$curl -o ~/.claude/skills/tdd-workflow-advanced/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/tdd-workflow-advanced/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/tdd-workflow-advanced/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How tdd-workflow-advanced Compares

Feature / Agenttdd-workflow-advancedStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/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 patterns

Related Skills

typescript-patterns-advanced

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.

slo-workflow

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

Advanced Rust patterns — zero-cost abstractions, proc macros, unsafe FFI, WASM, Axum web architecture, trait objects vs generics, and performance profiling.

python-testing-advanced

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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

8
from marvinrichter/clarc

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.