Unit Testing Methodologies

Background

Before refactoring a project, I wrote unit tests to ensure behavior would be preserved. Here are the key methodologies I used.

1. Error Handling at System Boundaries

Validate graceful handling of invalid input where your code meets external data.

describe('formatShortDate', () => {
  it('returns undefined for invalid date', () => {
    expect(formatShortDate('invalid-date')).toBeUndefined();
  });
});

2. Boundary Value Testing

Test at the edges of valid ranges where special handling is required.

Example: 12-hour time conversion

describe('formatTimeToAMPM', () => {
  it('handles midnight (00:00)', () => {
    expect(formatTimeToAMPM('00:00')).toBe('12:00 AM');
  });

  it('handles noon (12:00)', () => {
    expect(formatTimeToAMPM('12:00')).toBe('12:00 PM');
  });
});

Midnight and noon are boundaries—hour % 12 returns 0, requiring special logic to display "12" instead of "0".

3. Known Value Verification

For algorithms involving math or formulas, verify against known real-world values.

Avoid testing against self-derived values:

it('calculates distance', () => {
  const result = haversineDistance(toronto, montreal);
  expect(result).toBe(504.123); // Where did this come from?
});

If 504.123 came from running your implementation, you've just verified that your code returns what your code returns.

Example: Haversine distance calculation

describe('haversineDistance', () => {
  it('calculates Toronto to Montreal (~503 km)', () => {
    const toronto = { latitude: 43.65, longitude: -79.38 };
    const montreal = { latitude: 45.50, longitude: -73.57 };
    const distance = haversineDistance(toronto, montreal);
    expect(distance).toBeGreaterThan(498);
    expect(distance).toBeLessThan(508);
  });
});

This works because:

  • The Toronto–Montreal distance is a known, verifiable value
  • If the trigonometry or Earth's radius constant is wrong, the test fails

"Never use the same algorithm in the oracle that you are using for the system under test." — Test Oracle, Wikipedia

Summary

Focus tests on:

  • Error handling at system boundaries
  • Boundary values where special handling is needed
  • Known values to verify algorithm correctness

Reference