DevAcademy
LearnNode.jsTesting Node.js Apps
AdvancedNode.js

Testing Node.js Apps

Write unit and integration tests for a Node.js/Express API.

Reading Time

14 min

Lesson

Lesson 33 of 34

Unit vs Integration Tests

A unit test verifies a single function in isolation, often with dependencies mocked out. An integration test verifies multiple pieces working together — like an actual HTTP request hitting a real route, middleware, and (often) a real or test database.

A Unit Test (Vitest)

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

An Integration Test with supertest

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Common Testing Tools

ToolPurpose
Vitest / JestTest runner and assertion library
supertestSend HTTP requests directly to an Express app in tests, no real server needed
A test databaseA separate database instance, reset between test runs, so tests never touch production data

Testing an Express App Without a Live Server

supertest can send requests directly to your Express app object in memory, without actually binding to a port — this makes integration tests fast and avoids "is port 3000 already in use" conflicts in CI.

Structuring app.js vs server.js

A common pattern: app.js exports the configured Express app (routes, middleware) without calling .listen(), while a separate server.js imports it and calls .listen(). This lets tests import the app directly, without starting a real server.

Best Practice

Reset your test database between test runs (or use transactions that roll back) so tests remain independent and repeatable — a test that depends on data left behind by a previous test is fragile and hard to debug.

Interview Questions

Quick Quiz

1. What is the main difference between a unit test and an integration test?

2. What does supertest let you do?

3. Why is it common to keep app.listen() out of app.js and put it in a separate server.js?