Mock HTTP Request/Response for Node.js
mock-http is a Node.js library that provides robust mock implementations of the core `http.IncomingMessage` and `http.ServerResponse` classes. This enables developers to conduct isolated unit testing of HTTP server-side logic, such as Connect, Express, or Koa middleware, without the overhead of creating a real HTTP server or managing network sockets. The library ensures full API compatibility with Node.js's native `http` module interfaces, allowing middleware to be tested in an environment closely mirroring production. Currently stable at version 1.1.1, its release cadence is generally aligned with Node.js LTS cycles for compatibility updates, rather than frequent feature additions, indicating a mature and maintenance-focused project. Its key differentiator is the faithful emulation of the standard HTTP objects, providing a reliable and predictable testing surface for complex server-side components.
Common errors
-
TypeError: mock.Request is not a constructor
cause Attempting to instantiate `Request` or `Response` from an incorrectly imported `mock` object, or the `mock` object is undefined.fixEnsure you are importing the library correctly using named imports (`import { Request, Response } from 'mock-http';`) or a namespace import (`import * as mock from 'mock-http';`) and then referencing `mock.Request` or `mock.Response`. -
AssertionError: test never reaches here
cause The middleware being tested did not call `res.end()` or `next()`, which prevents the test's `onEnd` callback or the subsequent middleware function from executing.fixVerify the middleware logic correctly handles all execution paths, ensuring `res.end()` or `next()` is called under expected test conditions, especially for asynchronous operations. -
Error: write after end
cause The middleware attempted to write to the response stream (e.g., `res.write()`) after `res.end()` had already been called, which is not allowed by the `http.ServerResponse` contract.fixReview the middleware logic to prevent multiple calls to `res.end()` or any write operations to the response stream after the response has been finalized.
Warnings
- gotcha This library provides mock implementations of `http.IncomingMessage` and `http.ServerResponse`, meaning it does not involve actual network sockets. Functionality typically handled by the underlying socket, such as low-level `net.Socket` events or properties like `remoteAddress`, will not be accurately mimicked or may not function as expected in a real HTTP server environment.
- gotcha When testing asynchronous middleware, particularly those that process request bodies (e.g., `req.on('data')`, `req.on('end')`), it's crucial to ensure your test harness properly waits for the middleware's asynchronous operations to complete before asserting on the mock response state. Failure to do so can lead to flaky tests or incorrect assertions.
- gotcha The library exposes an `_internal` property on `mock.Response` for querying states like `headers`, `buffer`, and `ended`. While useful for testing, relying on the exact structure of this private property directly might be fragile if future major versions change its internal representation without explicit API changes.
Install
-
npm install mock-http -
yarn add mock-http -
pnpm add mock-http
Imports
- Request, Response
import Request from 'mock-http';
import { Request, Response } from 'mock-http'; - mock (namespace)
import mock from 'mock-http';
import * as mock from 'mock-http';
- Request (CJS), Response (CJS)
const mockHttp = require('mock-http'); const Request = mockHttp.Request;const { Request, Response } = require('mock-http');
Quickstart
import * as mock from 'mock-http';
import { strict as assert } from 'assert';
describe('mock-http example', function(){
// a middleware function under test
var middleware = function(req, res, next) {
var regex = /^(?:\/test)(\/.*|$)/;
req.params = '';
req.on('data', function(data){
req.params += data; // a simple body parser
});
req.on('end', function(){
if (regex.test(req.url)) {
req.url = req.url.replace(regex, '$1') || '/';
res.writeHead(200, { 'Cache-Control': 'max-age=300'});
res.write('this is a test');
res.end();
}
else {
next && next();
}
});
};
it('shall respond with a 200', function(done){
var req = new mock.Request({
url: '/test',
method: 'POST',
buffer: Buffer.from('name=mock&version=first')
});
var res = new mock.Response({
onEnd: function() {
// the test ends here
assert.equal(req.url, '/');
assert.equal(req.params, 'name=mock&version=first');
assert.equal(res.statusCode, 200);
assert.equal(res.headersSent, true);
assert.equal(res.getHeader('Cache-Control'), 'max-age=300');
assert.equal(res.hasEnded(), true);
done();
}
});
middleware(req, res, function(){
done(new Error('Next middleware should not have been called'));
});
});
it('shall call next middleware', function(done){
var req = new mock.Request({
url: '/other',
method: 'POST',
buffer: Buffer.from('data=something')
});
var res = new mock.Response({
onEnd: function() {
done(new Error('Response should not have ended, next middleware should be called'));
}
});
middleware(req, res, function(){
// This is the expected path for this test case
assert.equal(res.hasEnded(), false); // Ensure response was not ended by this middleware
done();
});
});
});