Supertest for JavaScript (Node.js): Complete Guide with Examples
Welcome to PythonLib! In this article, we'll dive deep into Supertest — one of the most popular HTTP testing tools in the Node.js ecosystem. Whether you're building web apps with Express, Koa, Fastify, or any other framework, Supertest will become your go-to tool for writing reliable integration tests.
1. What Is Supertest and Why Use It
Supertest is a high-level library for testing HTTP servers in Node.js. It provides a clean, expressive API for sending HTTP requests to your application and verifying responses. The key advantage of Supertest is that it lets you test your server without binding to a real port — the library uses Node.js internals to simulate HTTP connections, making tests fast and isolated.
Why do you need Supertest? In modern web development, code quality is paramount. Integration tests that verify your API endpoints help catch regressions, request handling bugs, and data validation issues. Supertest lets you write these tests concisely and readably using method chaining and built-in assertions for checking status codes, headers, response bodies, and more. The library works seamlessly with popular test runners like Jest, Mocha, Jasmine, and AVA.
Created by TJ Holowaychuk (the author of Express, Koa, and many other well-known projects), Supertest is maintained by the community. It's built on top of SuperAgent — a powerful HTTP client for Node.js and the browser. This means you get access to all of SuperAgent's features, including support for various data types, file uploads, cookie handling, and more.
2. Installation
Install Supertest via npm. It's recommended to add it as a devDependency since it's not needed in production.
npm install --save-dev supertest
If you're using yarn:
yarn add --dev supertest
For TypeScript users, you may also need to install the type definitions:
npm install --save-dev @types/supertest
Note that Supertest requires Node.js version 14 or higher. Also make sure you have a test runner installed (e.g., Jest or Mocha).
3. Quick Start — Minimal Working Example
Let's create a simple Express app and write a test for it using Supertest. This example will demonstrate the basic mechanics of the library.
First, create a file called app.js with our application:
// app.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.status(200).json({ message: 'Hello, World!' });
});
app.get('/user', (req, res) => {
res.status(200).json({ name: 'John Doe', age: 30 });
});
module.exports = app;
Now create a test file app.test.js:
// app.test.js
const request = require('supertest');
const app = require('./app');
describe('GET /', () => {
it('should return Hello, World!', async () => {
const response = await request(app)
.get('/')
.expect(200);
expect(response.body).toEqual({ message: 'Hello, World!' });
});
});
describe('GET /user', () => {
it('should return user data', async () => {
const response = await request(app)
.get('/user')
.expect('Content-Type', /json/)
.expect(200);
expect(response.body.name).toBe('John Doe');
expect(response.body.age).toBe(30);
});
});
Run the tests with your test runner (e.g., npx jest or npx mocha). You'll see both tests pass, confirming that Supertest is working correctly.
4. Core Methods and API
Supertest provides a rich set of methods for making HTTP requests and asserting responses. Here are the most commonly used ones:
HTTP Request Methods
.get(url)— Send a GET request.post(url)— Send a POST request.put(url)— Send a PUT request.patch(url)— Send a PATCH request.delete(url)— Send a DELETE request.head(url)— Send a HEAD request.options(url)— Send an OPTIONS request
Setting Request Data
.send(data)— Send JSON or form data in the request body.set(field, value)— Set a specific HTTP header.query(params)— Add query string parameters.field(name, value)— Set form fields (for multipart forms).attach(name, file)— Attach a file for upload
Assertions
.expect(status)— Assert the HTTP status code.expect(header, value)— Assert a specific header value.expect(body)— Assert the response body (can be a string, regex, or object).expect(fn)— Custom assertion function
5. Advanced Usage Examples
Testing POST Requests with JSON Body
describe('POST /users', () => {
it('should create a new user', async () => {
const newUser = { name: 'Jane Doe', email: 'jane@example.com' };
const response = await request(app)
.post('/users')
.send(newUser)
.expect(201)
.expect('Content-Type', /json/);
expect(response.body).toHaveProperty('id');
expect(response.body.name).toBe('Jane Doe');
});
});
Testing Authentication with Headers
describe('GET /protected', () => {
it('should return 401 without auth token', async () => {
await request(app)
.get('/protected')
.expect(401);
});
it('should return data with valid token', async () => {
const token = 'valid-jwt-token';
const response = await request(app)
.get('/protected')
.set('Authorization', `Bearer ${token}`)
.expect(200);
expect(response.body).toHaveProperty('data');
});
});
Testing File Uploads
const path = require('path');
describe('POST /upload', () => {
it('should upload a file', async () => {
const filePath = path.join(__dirname, 'test-file.txt');
const response = await request(app)
.post('/upload')
.attach('file', filePath)
.expect(200);
expect(response.body).toHaveProperty('filename');
});
});
6. Best Practices and Tips
- Always use
async/awaitfor cleaner, more readable test code - Chain assertions using
.expect()for concise tests - Test edge cases: missing fields, invalid data, authentication failures
- Use
beforeEachto set up test data or reset state - Keep tests isolated — each test should be independent
- Mock external services when testing API endpoints that call third-party APIs
- Use environment variables for configuration to make tests portable
7. Conclusion
Supertest is an essential tool for any Node.js developer serious about testing. Its intuitive API, seamless integration with popular test runners, and powerful assertion capabilities make it the go-to choice for HTTP testing. By incorporating Supertest into your workflow, you'll catch bugs earlier, improve code quality, and ship more reliable applications.
Start writing tests today — your future self (and your users) will thank you!