Unit Tests
This page documents the unit test suite for Express core modules. The tests validate routing, middleware, request/response extensions, configuration, and error handling. They are the primary mechanism for ensuring correctness during development and before merging changes.
Test Organization
The unit tests reside in the test directory at the repository root. Each test file corresponds to a specific module or feature of the Express library. The test suite contains 77 test files covering:
- Routing: route matching, parameters, route separation, error routing
- Middleware: execution order, error-handling middleware, middleware stacking
- Request/Response: HTTP method helpers, header manipulation, content negotiation, response formatting
- Configuration: application settings, view engine configuration, trust proxy
- Error handling: synchronous and asynchronous error propagation, error response formatting
- Edge cases: boundary conditions, malformed input, security-relevant scenarios
Each test file is independent and focuses on a single module or feature. Tests use a testing framework (likely Mocha) and supertest for HTTP assertions, as is standard in the Express ecosystem.
Testing Framework
The test suite uses the Mocha testing framework with supertest for HTTP-level assertions. The package.json scripts define the test runner commands:
| Script | Command | Purpose |
|---|---|---|
test | (defined in package.json) | Run the full test suite |
test-ci | (defined in package.json) | Run tests in CI mode (with coverage) |
test-cov | (defined in package.json) | Run tests with code coverage |
test-tap | (defined in package.json) | Run tests with TAP output |
The CI workflows use npm run test-ci for test execution .github/workflows/ci.yml:59-59.
Running Unit Tests
Prerequisites
- Node.js version 18 or higher (as specified in
package.jsonengines field) - npm (or compatible package manager)
Setup
# Install dependencies (including dev dependencies)
npm installRun Tests
# Run the full test suite
npm test
# Run tests with CI-style output (used in CI workflows)
npm run test-ci
# Run tests with code coverage
npm run test-cov
# Run tests with TAP output format
npm run test-tapRunning Tests on Specific Node Versions
The CI matrix tests against Node.js versions 18 through 26 on both Ubuntu and Windows .github/workflows/ci.yml:33-34. For local development, you can test against your current Node.js version using any of the above commands.
Code Coverage
When running npm run test-ci or npm run test-cov, the test runner generates coverage data in ./coverage/lcov.info. In CI, these coverage reports are:
- Uploaded as artifacts per matrix combination
.github/workflows/ci.yml:62-67 - Merged using
lcovinto a single report.github/workflows/ci.yml:82-85 - Uploaded to Coveralls for visibility
.github/workflows/ci.yml:87-90
Linting
Before running tests, contributors should run the linter to ensure code style consistency:
# Run linting
npm run lint
# Auto-fix linting issues
npm run lint:fixThe lint job in CI runs on ubuntu-latest with the latest LTS Node.js version .github/workflows/ci.yml:14-27.
CI Workflow Integration
The repository has four CI workflows that interact with the unit tests:
| Workflow | Trigger | Test Execution |
|---|---|---|
| ci | Push to master/develop/4.x/5.x/5.0, pull requests | Full test matrix (Node 18-26, Ubuntu + Windows) |
| legacy | Same branches as ci | Legacy Node.js versions (16, 17) |
| CodeQL | Push to master, pull requests to master, weekly schedule | Security analysis (no unit tests) |
| Scorecard | Branch protection rules, weekly schedule, push to master | Supply-chain security analysis (no unit tests) |
The ci workflow is the primary test pipeline. It runs linting first, then executes the test matrix with npm run test-ci .github/workflows/ci.yml:59-59. The legacy workflow runs the same test command but only on Node.js 16 and 17 .github/workflows/legacy.yml:33-34.
Both workflows use fail-fast: false to ensure all matrix combinations run even if some fail .github/workflows/ci.yml:31-31.
Test Coverage Areas
The unit tests cover the following Express core modules and features:
Application Layer
- Application creation and configuration
- Setting and getting application settings
- Mounting middleware and routes
- Error handling configuration
Routing
- Route definition (GET, POST, PUT, DELETE, etc.)
- Route parameters and pattern matching
- Route middleware execution order
- Route separation and modular routing
- Error routing (error-handling middleware with four arguments)
Middleware
- Middleware stacking and execution order
- Error-handling middleware (signature:
(err, req, res, next)) - Middleware that terminates the request-response cycle
- Middleware that calls
next()to pass control
Request Object
- HTTP method helpers (
req.get(),req.accepts(), etc.) - Header parsing and content negotiation
- URL and query string parsing
- Request body parsing (via body-parser integration)
Response Object
- Response methods (
res.send(),res.json(),res.render(), etc.) - Header setting and content type negotiation
- Status code setting
- Response formatting (JSON, HTML, plain text)
- ETag and caching headers
Configuration
- View engine configuration
- Template engine integration
- Trust proxy settings
- Case-sensitive routing
- Strict routing
Error Handling
- Synchronous error propagation
- Asynchronous error handling (via
next(err)) - Default error response formatting
- Custom error-handling middleware
Contributor Conventions
When contributing to Express, follow these testing conventions:
- Write tests for new features: Every new feature or bug fix should include corresponding unit tests in the
testdirectory. - Maintain test independence: Each test file should be self-contained and not depend on state from other tests.
- Use descriptive test names: Test descriptions should clearly indicate what behavior is being verified.
- Cover edge cases: Include tests for boundary conditions, error states, and security-relevant scenarios.
- Run linting first: Always run
npm run lintbefore submitting changes to ensure code style compliance. - Verify all tests pass: Run
npm testlocally before pushing changes. - Respect the CI matrix: Tests must pass on all supported Node.js versions and operating systems.