All skills
asyrafhussin avatar

/testing-best-practices

@575c801

Unit testing, integration testing, and test-driven development principles. Use when writing tests, reviewing test code, improving test coverage, or setting up testing strategy. Triggers on "write tests", "review tests", "testing best practices", or "TDD".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/testing-best-practices

This session only. Nothing lands on disk.

rulesassert-no-magic-numbers.md

≈1.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

No Magic Numbers in Assertions

Impact: HIGH (test self-documentation and maintainability)

Use named constants or explanatory variables instead of unexplained numeric literals. Name constants after their business meaning, not their value, and show derivation of expected values through calculation.

Incorrect

// ❌ Bad: Magic numbers obscure the meaning of tests
describe('DiscountCalculator', () => {
  test('calculates discount correctly', () => {
    const total = calculator.calculateTotal(100, 'SUMMER2024');

    expect(total).toBe(85); // Why 85? What's the discount?
  });

  test('applies maximum discount cap', () => {
    const discount = calculator.getDiscount(1000, 'MEGASALE');

    expect(discount).toBe(150); // Why is max 150?
  });

  test('handles loyalty points', () => {
    const points = loyaltyService.calculatePoints(250);

    expect(points).toBe(25); // What's the conversion rate?
  });
});

describe('PaginationService', () => {
  test('returns correct page', () => {
    const result = service.paginate(items, 2, 10);

    expect(result.items).toHaveLength(10);
    expect(result.totalPages).toBe(5); // Why 5?
    expect(result.startIndex).toBe(10); // Why 10?
    expect(result.endIndex).toBe(19);   // Why 19?
  });
});

Problems:

  • Numeric literals give no indication of what they represent
  • Reviewers cannot verify correctness without understanding the formula
  • Changing a business rule requires finding and updating scattered numbers
  • Debugging a failure requires reverse-engineering the expected value

Correct

// ✅ Good: Named constants explain the meaning of values
describe('DiscountCalculator', () => {
  const ORIGINAL_PRICE = 100;
  const SUMMER_DISCOUNT_PERCENT = 15;
  const EXPECTED_DISCOUNTED_PRICE = ORIGINAL_PRICE * (1 - SUMMER_DISCOUNT_PERCENT / 100);

  test('applies summer discount percentage to order total', () => {
    const total = calculator.calculateTotal(ORIGINAL_PRICE, 'SUMMER2024');

    expect(total).toBe(EXPECTED_DISCOUNTED_PRICE); // 85
  });

  const LARGE_ORDER_TOTAL = 1000;
  const MAXIMUM_DISCOUNT_CAP = 150;

  test('caps discount at maximum allowed amount', () => {
    const discount = calculator.getDiscount(LARGE_ORDER_TOTAL, 'MEGASALE');

    expect(discount).toBe(MAXIMUM_DISCOUNT_CAP);
  });

  const PURCHASE_AMOUNT = 250;
  const POINTS_PER_DOLLAR = 0.1;
  const EXPECTED_POINTS = PURCHASE_AMOUNT * POINTS_PER_DOLLAR;

  test('converts purchase amount to loyalty points', () => {
    const points = loyaltyService.calculatePoints(PURCHASE_AMOUNT);

    expect(points).toBe(EXPECTED_POINTS); // 25 points
  });
});

describe('PaginationService', () => {
  const TOTAL_ITEMS = 50;
  const PAGE_SIZE = 10;
  const CURRENT_PAGE = 2;

  const EXPECTED_TOTAL_PAGES = Math.ceil(TOTAL_ITEMS / PAGE_SIZE);
  const EXPECTED_START_INDEX = CURRENT_PAGE * PAGE_SIZE;
  const EXPECTED_END_INDEX = EXPECTED_START_INDEX + PAGE_SIZE - 1;

  let items: Item[];

  beforeEach(() => {
    items = Array.from({ length: TOTAL_ITEMS }, (_, i) => ({
      id: i + 1,
      name: `Item ${i + 1}`
    }));
  });

  test('returns correct page metadata', () => {
    const result = service.paginate(items, CURRENT_PAGE, PAGE_SIZE);

    expect(result.items).toHaveLength(PAGE_SIZE);
    expect(result.totalPages).toBe(EXPECTED_TOTAL_PAGES);
    expect(result.startIndex).toBe(EXPECTED_START_INDEX);
    expect(result.endIndex).toBe(EXPECTED_END_INDEX);
  });
});

describe('RateLimiter', () => {
  const RATE_LIMIT_PER_MINUTE = 100;
  const REQUESTS_AT_LIMIT = RATE_LIMIT_PER_MINUTE;
  const REQUESTS_EXCEEDING_LIMIT = RATE_LIMIT_PER_MINUTE + 1;

  test('allows requests up to the rate limit', async () => {
    for (let i = 0; i < REQUESTS_AT_LIMIT; i++) {
      await expect(rateLimiter.checkLimit('user-1')).resolves.not.toThrow();
    }
  });

  test('blocks requests exceeding the rate limit', async () => {
    for (let i = 0; i < REQUESTS_AT_LIMIT; i++) {
      await rateLimiter.checkLimit('user-2');
    }

    await expect(rateLimiter.checkLimit('user-2'))
      .rejects.toThrow(`Rate limit of ${RATE_LIMIT_PER_MINUTE} requests exceeded`);
  });
});

Benefits:

  • Constants are self-documenting and explain what values represent
  • Changing a value requires an update in only one place
  • Calculations show how expected values are derived so reviewers can verify logic
  • When tests fail, the constant names help understand what the numbers mean
  • Domain terminology in constant names improves readability

Reference: Clean Code — Chapter 17: Smells and Heuristics (Magic Numbers)

Source: SKILL.md on GitHub

No alerts16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides comprehensive guidelines and best practices for unit testing, integration testing, and Test-Driven Development (TDD). It consists entirely of documentation and illustrative code examples using industry-standard tools like Jest and Vitest. No security risks or malicious patterns were detected.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

  • Runlayer6mo

    29 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 575c801. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
metadata
{
  "author": "agent-skills",
  "version": "2.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/testing-best-practices