24.6k

This skill provides a comprehensive approach to defining and implementing effective testing strategies for software projects. It covers key concepts such as the test pyramid, unit, integration, and end-to-end testing, along with best practices like test isolation and TDD. Designed for developers and QA teams, it helps improve code quality through structured test planning, automation, and adherence to proven methodologies.

npx skills add https://github.com/supercent-io/skills-template --skill testing-strategies

Testing Strategies

When to use this skill

  • New project: define a testing strategy
  • Quality issues: bugs happen frequently
  • Before refactoring: build a safety net
  • CI/CD setup: automated tests

Instructions

Step 1: Understand the Test Pyramid

       /\
      /E2E\          ← few (slow, expensive)
     /______\
    /        \
   /Integration\    ← medium
  /____________\
 /              \
/   Unit Tests   \  ← many (fast, inexpensive)
/________________\

Ratio guide:

  • Unit: 70%
  • Integration: 20%
  • E2E: 10%

Step 2: Unit testing strategy

Given-When-Then pattern:

describe('calculateDiscount', () => {
  it('should apply 10% discount for orders over $100', () => {
    // Given: setup
    const order = { total: 150, customerId: '123' };
    // When: perform action
    const discount = calculateDiscount(order);
    // Then: verify result
    expect(discount).toBe(15);
  });
  it('should not apply discount for orders under $100', () => {
    const order = { total: 50, customerId: '123' };
    const discount = calculateDiscount(order);
    expect(discount).toBe(0);
  });
  it('should throw error for invalid order', () => {
    const order = { total: -10, customerId: '123' };
    expect(() => calculateDiscount(order)).toThrow('Invalid order');
  });
});

Mocking strategy:

// Mock external dependencies
jest.mock('../services/emailService');
import { sendEmail } from '../services/emailService';
describe('UserService', () => {
  it('should send welcome email on registration', async () => {
    // Arrange
    const mockSendEmail = sendEmail as jest.MockedFunction<typeof sendEmail>;
    mockSendEmail.mockResolvedValueOnce(true);
    // Act
    await userService.register({ email: 'test@example.com', password: 'pass' });
    // Assert
    expect(mockSendEmail).toHaveBeenCalledWith({
      to: 'test@example.com',
      subject: 'Welcome!',
      body: expect.any(String)
    });
  });
});

Step 3: Integration Testing

API endpoint tests:

describe('POST /api/users', () => {
  beforeEach(async () => {
    await db.user.deleteMany();  // Clean DB
  });
  it('should create user with valid data', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({
        email: 'test@example.com',
        username: 'testuser',
        password: 'Password123!'
      });
    expect(response.status).toBe(201);
    expect(response.body.user).toMatchObject({
      email: 'test@example.com',
      username: 'testuser'
    });
    // Verify it was actually saved to the DB
    const user = await db.user.findUnique({ where: { email: 'test@example.com' } });
    expect(user).toBeTruthy();
  });
  it('should reject duplicate email', async () => {
    // Create first user
    await request(app)
      .post('/api/users')
      .send({ email: 'test@example.com', username: 'user1', password: 'Pass123!' });
    // Attempt duplicate
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'test@example.com', username: 'user2', password: 'Pass123!' });
    expect(response.status).toBe(409);
  });
});

Step 4: E2E Testing (Playwright)

import { test, expect } from '@playwright/test';
test.describe('User Registration Flow', () => {
  test('should complete full registration process', async ({ page }) => {
    // 1. Visit homepage
    await page.goto('http://localhost:3000');
    // 2. Click Sign Up button
    await page.click('text=Sign Up');
    // 3. Fill out form
    await page.fill('input[name="email"]', 'test@example.com');
    await page.fill('input[name="username"]', 'testuser');
    await page.fill('input[name="password"]', 'Password123!');
    // 4. Submit
    await page.click('button[type="submit"]');
    // 5. Confirm success message
    await expect(page.locator('text=Welcome')).toBeVisible();
    // 6. Confirm redirect to dashboard
    await expect(page).toHaveURL('http://localhost:3000/dashboard');
    // 7. Confirm user info is displayed
    await expect(page.locator('text=testuser')).toBeVisible();
  });
  test('should show error for invalid email', async ({ page }) => {
    await page.goto('http://localhost:3000/signup');
    await page.fill('input[name="email"]', 'invalid-email');
    await page.fill('input[name="password"]', 'Password123!');
    await page.click('button[type="submit"]');
    await expect(page.locator('text=Invalid email')).toBeVisible();
  });
});

Step 5: TDD (Test-Driven Development)

Red-Green-Refactor Cycle:

// 1. RED: write a failing test
describe('isPalindrome', () => {
  it('should return true for palindrome', () => {
    expect(isPalindrome('racecar')).toBe(true);
  });
});
// 2. GREEN: minimal code to pass the test
function isPalindrome(str: string): boolean {
  return str === str.split('').reverse().join('');
}
// 3. REFACTOR: improve the code
function isPalindrome(str: string): boolean {
  const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
  return cleaned === cleaned.split('').reverse().join('');
}
// 4. Additional test cases
it('should ignore case and spaces', () => {
  expect(isPalindrome('A man a plan a canal Panama')).toBe(true);
});
it('should return false for non-palindrome', () => {
  expect(isPalindrome('hello')).toBe(false);
});

Output format

Testing strategy document

## Testing Strategy
### Coverage Goals
- Unit Tests: 80%
- Integration Tests: 60%
- E2E Tests: Critical user flows
### Test Execution
- Unit: Every commit (local + CI)
- Integration: Every PR
- E2E: Before deployment
### Tools
- Unit: Jest
- Integration: Supertest
- E2E: Playwright
- Coverage: Istanbul/nyc
### CI/CD Integration
- GitHub Actions: Run all tests on PR
- Fail build if coverage < 80%
- E2E tests on staging environment

Constraints

Required rules (MUST)

  1. Test isolation: each test is independent
  2. Fast feedback: unit tests should be fast (<1 min)
  3. Deterministic: same input → same result

Prohibited items (MUST NOT)

  1. Test dependencies: do not let test A depend on test B
  2. Production DB: do not use a real DB in tests
  3. Sleep/Timeout: avoid time-based tests

Best practices

  1. AAA pattern: Arrange-Act-Assert
  2. Test names: "should ... when ..."
  3. Edge Cases: boundary values, null, empty values
  4. Happy Path + Sad Path: both success/failure scenarios

References

Metadata

Version

  • Current version: 1.0.0
  • Last updated: 2025-01-01
  • Compatible platforms: Claude, ChatGPT, Gemini

Tags

#testing #test-strategy #TDD #unit-test #integration-test #E2E #code-quality

Examples

Example 1: Basic usage

Example 2: Advanced usage

GitHub Owner

Owner: goldbergyoni

Files

backend-testing

code-review

More skills