NJ Municipality Lookup
CodebaseSrc

__tests__

Test suite covering unit, integration, end-to-end, and code quality tests.

Test Suite (__tests__/)

Test suite covering unit, integration, end-to-end, and code quality tests.

Purpose

Ensures codebase quality, correctness, and maintainability through:

  1. Unit Tests: Isolated component and function testing
  2. Integration Tests: Multi-component workflows
  3. End-to-End Tests: Full user journeys via browser automation
  4. Code Quality Tests: Enforce architectural and documentation standards

Test Structure

__tests__/
β”œβ”€β”€ code-quality/         # Code standards and architectural rules
β”‚   β”œβ”€β”€ documentation-quality.test.ts
β”‚   β”œβ”€β”€ no-dynamic-imports.test.ts
β”‚   └── readme-coverage.test.ts
β”œβ”€β”€ e2e/                  # Browser-based end-to-end tests
β”‚   β”œβ”€β”€ acceptance.spec.ts
β”‚   β”œβ”€β”€ accessibility.spec.ts
β”‚   β”œβ”€β”€ edge-cases.spec.ts
β”‚   └── theme-toggle.spec.ts
└── helpers/              # Test utilities and fixtures
    β”œβ”€β”€ fake-services.ts
    β”œβ”€β”€ fake-services.test.ts
    β”œβ”€β”€ test-data-generators.ts
    └── test-data-generators.test.ts

Test Philosophy

Unit Tests (Co-located)

Most unit tests live next to their source files:

src/domain/entities/municipality.ts
src/domain/entities/municipality.test.ts  ← Unit test

src/adapters/cache/in-memory-cache.ts
src/adapters/cache/in-memory-cache.test.ts  ← Unit test

Benefits:

  • Easy to find related tests
  • Clear ownership and responsibility
  • Faster test discovery during development

Integration Tests (Feature-based)

Integration tests live in __tests__/ when they:

  • Span multiple modules
  • Test full workflows
  • Require complex setup/teardown

E2E Tests (Browser-based)

Playwright tests in __tests__/e2e/ simulate real user interactions:

  • Navigate actual routes
  • Fill forms and click buttons
  • Verify rendered output
  • Test accessibility

Code Quality Tests

Enforce non-negotiable standards:

  • Documentation coverage (every directory has README)
  • No dynamic imports (performance and security)
  • Consistent coding patterns

Test Coverage

Current coverage: 99.97% line coverage, 99.84% function coverage

Coverage by Layer

LayerLine CoverageFunction Coverage
Domain Entities100%100%
Domain Use Cases100%100%
Domain Errors100%100%
Adapters99.5%99.8%
Lib (Utilities)100%100%
App (Server Actions)98%98%

Uncovered Code

The 0.03% uncovered lines are:

  • Unreachable code required by TypeScript type checker
  • Marked with comments explaining why they're unreachable

Running Tests

# Run all tests
bun run test

# Run unit tests only (co-located + helpers)
bun run test --exclude e2e

# Run E2E tests
bun run test:e2e

# Run code quality tests
bun run test code-quality/

# Run with coverage
bun run test --coverage

# Watch mode
bun run test --watch

# Specific test file
bun run test municipality.test.ts

# Run tests matching pattern
bun run test geocoding

Test Utilities

Fake Services (helpers/fake-services.ts)

Fake implementations (not mocks) for testing without external dependencies:

import {
  createFakeGeocodingService,
  createFakeCache,
} from "@/__tests__/helpers/fake-services";

// Returns predictable results without API calls
const geocoder = createFakeGeocodingService();
const result = await geocoder.geocodeAddress(address);

// In-memory cache for testing
const cache = createFakeCache();
await cache.set("key", "value");

Why Fakes vs Mocks?

  • Fakes have working implementations
  • Tests verify actual behavior, not just method calls
  • More resilient to refactoring

Test Data Generators (helpers/test-data-generators.ts)

Concise generators for creating test data:

import {
  genAddress,
  genMunicipality,
  genGeocodingResult,
} from "@/__tests__/helpers/test-data-generators";

// Generate test address (uses real NJ addresses)
const address = genAddress(REAL_NJ_ADDRESSES.njit);

// Generate municipality
const municipality = genMunicipality("Newark", "Essex", "City");

// Generate geocoding result
const result = genGeocodingResult({ municipality });

Real NJ Addresses:

Test data uses actual NJ government addresses:

  • NJIT: 323 Dr Martin Luther King Jr Blvd, Newark
  • State House: 125 West State Street, Trenton
  • Drumthwacket: 354 Stockton Street, Princeton
  • Rutgers: 83 Somerset Street, New Brunswick
  • NJCU: 2039 John F Kennedy Boulevard, Jersey City

This ensures tests work with real geocoding API responses.

Test Patterns

Unit Test Pattern

import { expect, test, describe } from "bun:test";

describe("createMunicipality", () => {
  test("should create valid municipality", () => {
    const muni = createMunicipality({
      name: "Newark",
      county: "Essex",
      type: "City",
    });

    expect(muni.name).toBe("Newark");
    expect(muni.county).toBe("Essex");
    expect(muni.type).toBe("City");
  });

  test("should reject invalid input", () => {
    expect(() =>
      createMunicipality({ name: "", county: "Essex", type: "City" }),
    ).toThrow();
  });
});

Integration Test Pattern

test("should lookup address with cache", async () => {
  const cache = createFakeCache();
  const geocoder = createFakeGeocodingService();
  const useCase = createLookupAddressUseCase(geocoder, cache);

  const address = genAddress();
  const result = await useCase.execute(address);

  expect(result.municipality.name).toBe("Newark");
  expect(result.fromCache).toBe(false);

  // Second call should hit cache
  const cachedResult = await useCase.execute(address);
  expect(cachedResult.fromCache).toBe(true);
});

E2E Test Pattern

import { test, expect } from "@playwright/test";

test("should display municipality on successful lookup", async ({ page }) => {
  await page.goto("/lookup");

  await page
    .getByRole("textbox", { name: /street/i })
    .fill("323 Dr Martin Luther King Jr Blvd");
  await page.getByRole("textbox", { name: /city/i }).fill("Newark");
  await page.getByRole("textbox", { name: /zip/i }).fill("07102");

  await page.getByRole("button", { name: /lookup/i }).click();

  await expect(page.getByText(/municipality/i)).toBeVisible();
  await expect(page.getByText(/newark/i)).toBeVisible();
  await expect(page.getByText(/essex/i)).toBeVisible();
});

Continuous Integration

Tests run on every commit via GitHub Actions:

- name: Run tests
  run: bun run test --coverage

- name: Run E2E tests
  run: bun run test:e2e

- name: Check coverage threshold
  run: bun coverage --check-coverage --lines 95

On this page