Automated API Testing
Configure automated testing, write HTTP integration tests using Supertest, test CRUD endpoints, and handle database isolation.
Understand API Testing
API testing verifies that backend endpoints behave as expected.
It helps identify issues before the application reaches production.
Common API testing categories include:
Unit testing
Tests individual functions or modules in isolation.
Integration testing
Tests how multiple application components work together.
End-to-end testing
Tests complete application workflows through the public interface.
For our REST API, we will use Supertest to send HTTP requests directly to the Express application.
We will use Jest as the test runner.
Because the application uses ES Modules, Jest requires compatible configuration.
API testing verifies endpoint behavior.
Integration tests validate multiple connected components.
Supertest allows HTTP testing without manually opening a browser.
Configure Jest for ES Modules
Our application uses ES Modules, so configure Jest to run in ESM mode.
Update the package.json scripts.
The experimental VM modules flag is used by Jest for its native ESM support in this configuration.
Use a Jest release compatible with your Node.js version.
If you prefer to avoid Jest's ESM configuration complexity, Node.js provides a built-in test runner through node:test.
{
"scripts": {
"start": "node src/server.js",
"dev": "node --watch src/server.js",
"test": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand"
}
}Configures Jest to execute in ESM mode and runs tests sequentially. On Windows, use cross-env or a platform-specific environment variable command if required.
Jest requires ESM-compatible configuration.
The VM modules flag enables native ESM support in this setup.
Keep testing configuration separate from production scripts.
Create an Isolated Test Database
Automated tests should not modify your development or production database.
Create a separate test database.
For example:
MONGODB_TEST_URI=mongodb://127.0.0.1:27017/rest_api_test
A separate database allows tests to create and delete test records without affecting normal application data.
For larger test suites, consider an isolated MongoDB instance or a dedicated test database per test worker.
Never execute test cleanup commands against a production database. Verify the active database name before dropping collections or deleting test data.
NODE_ENV=test
MONGODB_TEST_URI=mongodb://127.0.0.1:27017/rest_api_test
JWT_SECRET=test-only-secretDefines environment variables for local automated testing. Never reuse production credentials or databases in tests.
Use a separate test database.
Do not run destructive tests against production.
Keep test configuration isolated.
Write User API Integration Tests
Create tests/users.test.js.
Supertest accepts an Express application and allows you to send HTTP requests without manually starting the application on a fixed port.
We will test:
- Health endpoint.
- User registration.
- Login.
- Invalid login.
- Protected endpoint access.
For this example, assume the test setup connects to the isolated test database and clears test records between test runs.
import request from 'supertest';
import mongoose from 'mongoose';
import app from '../src/app.js';
import { User } from '../src/models/user.model.js';
import { connectDatabase } from '../src/config/database.js';
beforeAll(async () => {
process.env.MONGODB_URI = process.env.MONGODB_TEST_URI;
await connectDatabase();
});
afterEach(async () => {
await User.deleteMany({});
});
afterAll(async () => {
await mongoose.disconnect();
});
describe('Authentication API', () => {
test('should register a new user', async () => {
const response = await request(app)
.post('/api/v1/auth/register')
.send({
name: 'John Doe',
email: 'john@example.com',
password: 'SecurePassword123!'
});
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(response.body.data.password).toBeUndefined();
});
test('should reject invalid login credentials', async () => {
const response = await request(app)
.post('/api/v1/auth/login')
.send({
email: 'unknown@example.com',
password: 'wrong-password'
});
expect(response.status).toBe(401);
});
});Creates integration tests using Supertest. The example assumes the test environment is configured before Jest imports the application and that the authentication routes are registered.
Supertest can test Express applications directly.
Use an isolated database for integration tests.
Verify both successful and unsuccessful API requests.