supertest: HTTP assertions for Node.js servers without binding a port
đź•· Super-agent driven library for testing node.js HTTP servers using a fluent API. Maintained for @forwardemail, @ladjs, @spamscanner, @breejs, @cabinjs, and @lassjs.
At a glance
- What is it?
- supertest wraps superagent in a fluent assertion chain and can drive an http.Server or an app function directly, binding an ephemeral port when the server is not already listening. It is a test-time HTTP client, not a test runner, and its error handling has sharp edges worth knowing before you adopt it.
- Who is it for?
- Adopt supertest if you already write Node.js integration tests and want status, header and body assertions in one chain without managing ports; the README's own examples show it works with mocha, with promises and with async/await, and with no framework at all. Do not adopt it as a replacement for a test runner, and do not expect it to test browser behaviour or non-HTTP transports.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What supertest is for, and who is on the other end of it
supertest exists to make HTTP assertions readable in a Node.js test file. The README states the motivation directly: provide a high-level abstraction for testing HTTP, while still allowing you to drop down to the lower-level API provided by superagent. That sentence describes the whole design. You get a chain of .expect() calls for the common cases, and when a case is not common you keep superagent's request object and its methods.
The audience is narrow and specific. You are writing tests for an HTTP server you control, in JavaScript, and you want to assert on the response without starting that server on a fixed port and cleaning it up afterwards. The README's example passes an Express app straight into request(app) and notes that if the server is not already listening for connections then it is bound to an ephemeral port for you, so there is no need to keep track of ports. That single behaviour removes the most annoying piece of boilerplate in Node HTTP integration tests.
It is not a test runner. The README says supertest works with any test framework and then shows an example without using any test framework at all, calling .end() and throwing on error. If you are looking for a runner, an assertion library for plain values, or a browser automation tool, this is the wrong package.
How the chain works: Test objects, ordered expectations and superagent underneath
The repository layout is small: index.js, a lib/ directory, and a test/ directory, with dependencies limited to methods, superagent and cookie-signature. The mechanism visible from the README is a fluent chain built on superagent. Each request.VERB() call creates a new Test instance, and the README notes you can re-assign the request variable with the initialization app or url so you do not pass the host on every call.
Expectations run in the order of definition. The README treats this as a feature and shows why: you can insert a function expectation that mutates the response body before a later assertion checks it, lowercasing a name field and pinning an id. That ordering is the part of the design most worth understanding, because it means an .expect() is not a pure predicate. It is a step in a pipeline, and a step can change the object the next step sees.
Two protocol details appear in the README. HTTP/2 is opt-in by appending an options object to request or request.agent, for example request(app, { http2: true }), and the README shows the same option on request.agent(app, { http2: true }). Cookies are handled through request.agent(app), which the README demonstrates persisting a request and its cookies across calls using cookie-parser in the example app. The cookie-signature dependency is consistent with that agent behaviour, though the README does not spell out the signing path.
Installing supertest and a first runnable test
The README gives one install command and says to save it as a development dependency. After that, the module is referenced with require('supertest').
npm install supertest --save-devThe first example in the README uses Express and no test framework at all. It defines a single route, chains .expect() for the content type, the content length and the status code, and ends with a callback that rethrows any error.
const request = require('supertest');
const express = require('express');
const app = express();
app.get('/user', function(req, res) {
res.status(200).json({ name: 'john' });
});
request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect('Content-Length', '15')
.expect(200)
.end(function(err, res) {
if (err) throw err;
});Running that file should exit quietly when the route matches. Note the content length assertion: the body is exactly { name: 'john' }, which is 15 bytes, so this example breaks the moment you change the payload. That is the README's example, not a recommendation.
Inside a runner, the README shows mocha with done passed straight to an .expect() call, and separately shows promise and async/await forms. The async form is the least ceremonious, because you await the request and then assert with whatever assertion library you already use:
describe('GET /users', function() {
it('responds with json', async function() {
const response = await request(app)
.get('/users')
.set('Accept', 'application/json')
expect(response.headers["content-type"]).toMatch(/json/);
expect(response.status).toEqual(200);
expect(response.body.email).toEqual('[email protected]');
});
});For authenticated endpoints, the README documents an auth method that takes a username and password and behaves the same way as superagent's authentication. For multipart uploads it points at superagent: .field() for form fields, including a contentType option for JSON-valued fields, and .attach() for a file path from your fixtures directory.
The error-handling footgun the README warns about twice
This is the limitation that decides whether supertest fits your suite. The README states that if you use the .end() method, .expect() assertions that fail will not throw; they return the assertion as an error to the .end() callback. To fail the test case you must rethrow or pass the error to done(). The README's own POST example does exactly that, ending with if (err) return done(err); return done();.
If you skim the first example and copy the .end(function(err, res) { if (err) throw err; }) shape without a runner that catches the throw, a failing assertion can look like a passing test. The safer path is the promise or async/await form, where a failed assertion rejects and your runner reports it. That is a real trade-off: the callback style reads more compactly in the README, and the promise style is the one that fails loudly by default.
The second warning is about status codes. The README notes that superagent sends any HTTP error, anything other than a 2XX response code, to the callback as the first argument if you do not add a status code expect. So a test that only checks a response body on a 302 or a 500 will receive an error instead of the response, unless you assert the status explicitly with something like .expect(302). If you are testing redirects or error paths, add the status expectation first.
A third boundary is scope. supertest drives HTTP servers. It has no browser, so it will not execute client-side JavaScript, and it does not speak non-HTTP transports. For anything below the HTTP layer, or for a page that renders in a DOM, it is the wrong tool.
supertest compared with plain superagent and with a full test runner
The honest alternative is superagent itself, which supertest depends on and which the README links as the lower-level API. With superagent alone you build the request, start your server yourself, pick a port, call .end() or await the request, and assert with your own assertion library. That is more code per test, and it is also more explicit: nothing about ordering, nothing about a Test object wrapping your assertions, and no ambiguity about where a failure surfaces.
supertest's value is concentrated in two behaviours that superagent does not provide on its own. First, passing an app or a server and letting supertest bind an ephemeral port when the server is not already listening. Second, the .expect() chain with defined ordering, including function expectations that can mutate the response before later assertions run. If neither matters to you, superagent plus your existing assertion library is fewer moving parts.
The other comparison people reach for is a test runner such as mocha, which appears in supertest's own devDependencies and in the README examples. These are not alternatives. mocha decides which tests run and reports results; supertest makes the HTTP request and asserts on the response. A useful way to read the README's mocha examples is as the intended pairing: supertest for the request, the runner for the verdict.
One caveat on the comparison. The README does not document rollback, retries or a record-and-replay mode, and the package has no such features in its dependency list. If you need recorded fixtures, nock appears in the devDependencies of the project itself, but the README does not present it as part of supertest's API.
Maintenance, upgrade cost and the MIT licence
The repository is not archived. The last push was on 2026-04-02, and the most recent release listed is v7.2.2 from 2026-01-06, with v7.2.1 and v7.2.0 published earlier the same day. The project is maintained for the Forward Email and Lad organisations, and the README names a set of related projects it is kept for.
Upgrade cost is shaped by the dependency on superagent, pinned in package.json as ^10.3.0. A caret range means a superagent minor release can change behaviour under supertest without a supertest release, and supertest's own surface is thin enough that most of what you rely on is superagent behaviour. Pinning superagent in a lockfile is the practical way to keep test runs reproducible across installs. The other two dependencies, methods and cookie-signature, are small and versioned with carets as well.
The engines field requires Node.js >=14.18.0, so older runtimes are out of scope. The published package ships only index.js and lib, per the files field, which keeps install size down. The test script runs nyc with mocha, requires should, and passes --check-leaks, so the project's own suite is written to catch leaked globals. That is a detail about the project's tests, not a guarantee about yours.
The licence is MIT. That is permissive and short, and it is the same licence family most Node tooling uses. Nothing in the repository's package.json indicates a dual licence, a commercial tier or a contributor agreement that would change how you can ship it; for anything beyond that, read the LICENSE file in the repository rather than this article.
Editorial conclusion
Adopt supertest if you already write Node.js integration tests and want status, header and body assertions in one chain without managing ports; the README's own examples show it works with mocha, with promises and with async/await, and with no framework at all. Do not adopt it as a replacement for a test runner, and do not expect it to test browser behaviour or non-HTTP transports. Before committing, verify three things against your own suite: whether every failing assertion reaches your test runner (the README warns that .expect() failures under .end() are returned to the callback rather than thrown), whether your Node version satisfies the engines field of >=14.18.0, and whether you need the http2 option, which is documented only for request and request.agent.
Frequently asked questions
What is supertest used for?
It is used to test Node.js HTTP servers by making requests and asserting on the response in a fluent chain. The README describes it as a high-level abstraction for testing HTTP that still lets you drop down to superagent's lower-level API.
How do I install supertest?
The README gives one command, npm install supertest --save-dev, which saves it to package.json as a development dependency. After that you reference it with require('supertest').
How do I use supertest in Node.js?
Pass an http.Server or a function to request(), then chain a verb, headers and .expect() assertions. If the server is not already listening, the README states it is bound to an ephemeral port for you, so you do not track ports yourself.
What is the difference between Jest and supertest?
The README does not discuss Jest. supertest is an HTTP request and assertion library, and the README says it works with any test framework, showing examples with mocha and an example with no framework at all.
What is supertest npm?
It is the npm package named supertest, currently at version 7.2.2 according to package.json, with superagent, methods and cookie-signature as its dependencies. Its main entry is index.js and it requires Node.js >=14.18.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/forwardemail-supertest)