Agent Skills: Testing Skill

Testing patterns, strategies, and best practices for comprehensive test coverage.

UncategorizedID: az9713/claude-code-agentic-framework/testing

Install this agent skill to your local

pnpm dlx add-skill https://github.com/az9713/claude-code-agentic-framework/tree/HEAD/skills/testing

Skill Files

Browse the full folder contents for testing.

Download Skill

Loading file tree…

skills/testing/SKILL.md

Skill Metadata

Name
testing
Description
Testing patterns, strategies, and best practices for comprehensive test coverage.

Testing Skill

Overview

This skill defines testing patterns, strategies, and best practices for achieving comprehensive test coverage across the project.

Test Types

| Type | Purpose | Location | Speed | |------|---------|----------|-------| | Unit | Test isolated functions | tests/unit/ | Fast | | Integration | Test component interactions | tests/integration/ | Medium | | E2E | Test full user flows | tests/e2e/ | Slow |

Directory Structure

tests/
├── unit/
│   ├── utils/
│   │   └── helpers.test.js
│   └── services/
│       └── userService.test.js
├── integration/
│   ├── api/
│   │   └── userRoutes.test.js
│   └── database/
│       └── userRepository.test.js
├── e2e/
│   └── userFlow.test.js
├── fixtures/
│   └── testData.js
├── helpers/
│   └── testUtils.js
└── run.js

Test File Naming

// Unit tests
[module].test.js
[module].spec.js

// Integration tests
[feature].integration.test.js

// E2E tests
[flow].e2e.test.js

Writing Tests

Basic Test Structure

const { functionToTest } = require('../../app/module');

describe('ModuleName', () => {
  describe('functionToTest', () => {
    // Setup
    beforeEach(() => {
      // Reset state before each test
    });

    afterEach(() => {
      // Cleanup after each test
    });

    // Happy path tests
    describe('when given valid input', () => {
      test('returns expected result', () => {
        const result = functionToTest('valid');
        expect(result).toBe('expected');
      });
    });

    // Edge cases
    describe('edge cases', () => {
      test('handles empty input', () => {
        expect(functionToTest('')).toBe('default');
      });

      test('handles null input', () => {
        expect(functionToTest(null)).toBeNull();
      });
    });

    // Error cases
    describe('error handling', () => {
      test('throws on invalid input', () => {
        expect(() => functionToTest(undefined)).toThrow();
      });
    });
  });
});

AAA Pattern

test('calculateTotal returns correct sum with discount', () => {
  // Arrange - Setup test data
  const items = [{ price: 100 }, { price: 50 }];
  const discount = 0.1;

  // Act - Execute the code
  const result = calculateTotal(items, discount);

  // Assert - Verify the result
  expect(result).toBe(135); // (100 + 50) * 0.9
});

Common Assertions

// Equality
expect(value).toBe(expected);           // Strict equality
expect(value).toEqual(expected);        // Deep equality
expect(value).not.toBe(unexpected);     // Negation

// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();

// Numbers
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThan(5);
expect(value).toBeCloseTo(0.3, 5);      // For floating point

// Strings
expect(string).toMatch(/pattern/);
expect(string).toContain('substring');

// Arrays
expect(array).toContain(item);
expect(array).toHaveLength(3);

// Objects
expect(object).toHaveProperty('key');
expect(object).toHaveProperty('key', 'value');
expect(object).toMatchObject({ partial: 'match' });

// Errors
expect(() => fn()).toThrow();
expect(() => fn()).toThrow(Error);
expect(() => fn()).toThrow('message');

// Async
await expect(asyncFn()).resolves.toBe(expected);
await expect(asyncFn()).rejects.toThrow();

Testing Async Code

Promises

test('async function resolves correctly', async () => {
  const result = await asyncFunction();
  expect(result).toBe('expected');
});

test('async function rejects on error', async () => {
  await expect(asyncFunction('bad')).rejects.toThrow('Error');
});

Callbacks

test('callback is called with result', (done) => {
  callbackFunction((result) => {
    expect(result).toBe('expected');
    done();
  });
});

Mocking

Mock Functions

const mockFn = jest.fn();

// Set return value
mockFn.mockReturnValue('value');
mockFn.mockResolvedValue('async value');
mockFn.mockRejectedValue(new Error('error'));

// Verify calls
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledTimes(2);

Mock Modules

// Mock entire module
jest.mock('../path/to/module');

// Mock with implementation
jest.mock('../path/to/module', () => ({
  functionA: jest.fn().mockReturnValue('mocked'),
  functionB: jest.fn()
}));

// Clear mocks between tests
beforeEach(() => {
  jest.clearAllMocks();
});

Mock Time

beforeEach(() => {
  jest.useFakeTimers();
});

afterEach(() => {
  jest.useRealTimers();
});

test('timeout behavior', () => {
  const callback = jest.fn();
  setTimeout(callback, 1000);

  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalled();
});

Test Fixtures

// tests/fixtures/testData.js
module.exports = {
  validUser: {
    id: 1,
    name: 'Test User',
    email: 'test@example.com'
  },
  invalidUser: {
    id: null,
    name: '',
    email: 'invalid-email'
  }
};

// Usage in tests
const { validUser, invalidUser } = require('../fixtures/testData');

test('validates user correctly', () => {
  expect(validateUser(validUser)).toBe(true);
  expect(validateUser(invalidUser)).toBe(false);
});

Integration Tests

const request = require('supertest');
const app = require('../../app');
const db = require('../../app/database');

describe('User API', () => {
  beforeAll(async () => {
    await db.connect();
  });

  afterAll(async () => {
    await db.close();
  });

  beforeEach(async () => {
    await db.clear('users');
  });

  test('POST /users creates new user', async () => {
    const response = await request(app)
      .post('/users')
      .send({ name: 'Test', email: 'test@example.com' })
      .expect(201);

    expect(response.body).toHaveProperty('id');
    expect(response.body.name).toBe('Test');
  });

  test('GET /users/:id returns user', async () => {
    const user = await db.insert('users', { name: 'Test' });

    const response = await request(app)
      .get(`/users/${user.id}`)
      .expect(200);

    expect(response.body.name).toBe('Test');
  });
});

Coverage Goals

| Category | Target | |----------|--------| | Critical paths | 100% | | Business logic | 90%+ | | Error handling | 80%+ | | Edge cases | 70%+ | | Utilities | 50%+ |

Running Tests

# Run all tests
npm test

# Run specific file
npm test -- tests/unit/utils.test.js

# Run with coverage
npm test -- --coverage

# Watch mode
npm test -- --watch

# Run only changed tests
npm test -- --onlyChanged

Test Quality Checklist

  • [ ] Tests are independent (no shared state)
  • [ ] Tests are deterministic (same result every run)
  • [ ] Tests are fast (< 100ms for unit tests)
  • [ ] Tests are readable (clear what's being tested)
  • [ ] Tests cover happy path
  • [ ] Tests cover edge cases
  • [ ] Tests cover error cases
  • [ ] Mocks are appropriate (not over-mocking)
  • [ ] No console.log in tests
  • [ ] No skipped tests without explanation