testing-patterns

nategarelik's avatarfrom nategarelik

Auto-load when writing tests. Provides TDD workflow, test structure patterns, and common testing idioms.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 2, 2026

When & Why to Use This Skill

This Claude skill provides a comprehensive framework for implementing Test-Driven Development (TDD) and high-quality software testing. It offers standardized patterns like AAA (Arrange-Act-Assert), guidance on mocking dependencies, and best practices for test naming and coverage to ensure robust, maintainable codebases.

Use Cases

  • Implementing the Red-Green-Refactor workflow to ensure code correctness from the start of development.
  • Structuring unit tests using the AAA (Arrange-Act-Assert) pattern to improve test readability and maintenance.
  • Determining testing priorities by identifying critical business logic and edge cases while avoiding redundant framework testing.
  • Configuring mocks and dependency injection to isolate units of code and handle external API dependencies effectively.
  • Establishing standardized test naming conventions and coverage targets to maintain high code quality across engineering teams.
nametesting-patterns
description"Auto-load when writing tests. Provides TDD workflow, test structure patterns, and common testing idioms."

Testing Patterns

TDD Workflow (RED-GREEN-REFACTOR)

1. RED: Write Failing Test First

describe('Calculator', () => {
  it('should add two numbers', () => {
    const calc = new Calculator();
    expect(calc.add(2, 3)).toBe(5);
  });
});

Run test - verify it FAILS for the right reason.

2. GREEN: Minimal Implementation

class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

Run test - verify it PASSES.

3. REFACTOR: Clean Up

Improve code while keeping tests green.

Test Structure (AAA Pattern)

it('should transfer money between accounts', () => {
  // Arrange - Set up test data
  const source = new Account(100);
  const target = new Account(0);

  // Act - Perform the action
  source.transfer(50, target);

  // Assert - Verify results
  expect(source.balance).toBe(50);
  expect(target.balance).toBe(50);
});

What to Test

DO Test

  • Business logic and calculations
  • Edge cases and boundaries
  • Error handling paths
  • State transitions
  • Integration points

DON'T Test

  • Framework code (React, Express)
  • Third-party libraries
  • Simple getters/setters
  • Implementation details

Mocking Guidelines

// Mock external dependencies
jest.mock('./api', () => ({
  fetchUser: jest.fn().mockResolvedValue({ id: 1, name: 'Test' }),
}));

// Prefer dependency injection over mocking
class UserService {
  constructor(private api: ApiClient) {}

  async getUser(id: number) {
    return this.api.fetchUser(id);
  }
}

// In tests - inject mock
const mockApi = { fetchUser: jest.fn() };
const service = new UserService(mockApi);

Test Naming Convention

describe('[Unit Under Test]', () => {
  describe('[Method/Scenario]', () => {
    it('should [expected behavior] when [condition]', () => {
      // test
    });
  });
});

Async Testing

// Async/await (preferred)
it('should fetch user', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('Test');
});

// Testing rejections
it('should throw on invalid id', async () => {
  await expect(fetchUser(-1)).rejects.toThrow('Invalid ID');
});

Coverage Targets

  • Aim for 80% coverage on business logic
  • 100% coverage on critical paths (auth, payments)
  • Don't chase 100% everywhere - diminishing returns