hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Testing APIs with @hectoday/http

Why Test

  • What Testing Gives You
  • Types of Tests
  • Project Setup

Unit Testing

  • Testing Pure Functions
  • Testing Zod Schemas
  • Testing Business Logic

Integration Testing

  • Testing Route Handlers
  • Testing GET Endpoints
  • Testing POST Endpoints
  • Testing Error Responses
  • Testing Authentication

Test Helpers

  • Factories and Fixtures
  • Test Database Isolation
  • Request Helpers

Advanced Testing

  • Mocking External Services
  • Testing Background Jobs
  • Testing Edge Cases

Putting It All Together

  • Test Organization
  • Checklist and Capstone

Test Organization

File structure

Organize tests to mirror the source code:

tests/
  setup.ts                        # Global setup (test database, env)
  helpers/
    factories.ts                  # createAuthor, createBook, createReview
    request.ts                    # get, post, put, del, jsonRequest
    auth.ts                       # createTestToken, authHeader
    mocks.ts                      # createEmailMock, createPaymentMock
  unit/
    transformers.test.ts          # formatBookV1, formatBookV2
    schemas.test.ts               # Zod schema validation
    helpers.test.ts               # slugify, paginate, etc.
  integration/
    books.test.ts                 # Book CRUD endpoints
    reviews.test.ts               # Review endpoints
    auth.test.ts                  # Protected endpoints, login
    errors.test.ts                # Error response consistency
    versioning.test.ts            # v1 vs v2 responses
  advanced/
    edge-cases.test.ts            # Boundary values, special chars
    background-jobs.test.ts       # Job enqueueing and handling

Unit tests in unit/. Integration tests in integration/. Each test file corresponds to a feature or component.

Naming conventions

Test files: feature.test.ts. Match the source file: transformers.ts → transformers.test.ts.

Describe blocks: Name the function or endpoint being tested.

Test names: Describe the expected behavior, not the implementation.

// GOOD: describes behavior
test("returns 404 for nonexistent book");
test("trims whitespace from title before saving");
test("rejects rating below 1");

// BAD: describes implementation
test("calls db.prepare with SELECT");
test("runs trim() on the title string");
test("checks if rating < 1");

Running subsets

# Run all tests
npm test

# Run only unit tests
npx vitest run tests/unit

# Run only integration tests
npx vitest run tests/integration

# Run a specific test file
npx vitest run tests/integration/books.test.ts

# Run tests matching a pattern
npx vitest run -t "returns 404"

# Watch mode (re-runs on file changes)
npx vitest

Running subsets is useful during development: work on the books endpoint → run only books.test.ts. Before committing → run all tests.

Test scripts in package.json

{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest",
    "test:unit": "vitest run tests/unit",
    "test:integration": "vitest run tests/integration",
    "test:coverage": "vitest run --coverage"
  }
}

CI integration

Tests should run on every push. A basic GitHub Actions workflow:

name: Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npm test

[!NOTE] The Deploying with Docker course set up CI/CD pipelines. Adding npm test to the pipeline ensures tests run before every deploy — bugs are caught before they reach production.

How many tests is enough?

There is no magic number. A practical guideline:

Every endpoint should have at least: one happy path test (200/201), one validation failure test (400), one not found test (404 for detail endpoints).

Every business rule should have a test: “Users cannot review their own books.” “Admins can delete books.”

Every bug fix should come with a test that fails before the fix and passes after. This prevents the bug from returning.

Exercises

Exercise 1: Organize your tests into the file structure above. Run unit and integration tests separately.

Exercise 2: Add test scripts to package.json. Run each one.

Exercise 3: Add a CI configuration that runs tests on every push.

Why should test names describe behavior, not implementation?

← Testing Edge Cases Checklist and Capstone →

© 2026 hectoday. All rights reserved.