Set up backend unit tests in Replit by installing a testing framework (Jest for Node.js or pytest for Python), writing test files for your API endpoints, and configuring a test script in package.json or .replit. Run tests from Shell with 'npm test' or 'pytest'. Structure your tests to cover route handlers, database operations, and error cases. Configure the .replit file to run tests before deployment.
Write and Run Backend API Unit Tests in Replit
This tutorial shows you how to set up a testing framework, write meaningful unit tests for your backend API, and run them inside Replit. You will install Jest (for Node.js) or pytest (for Python), write tests that validate API endpoints, mock external dependencies, and configure your project so tests run reliably. This guide is for developers who want to catch bugs before they reach production.
Prerequisites
- A Replit account (any plan)
- A working backend API project (Express.js or Flask)
- Basic understanding of HTTP methods and REST API patterns
- Familiarity with the Replit Shell tab
- No prior testing experience required
Step-by-step guide
Install your testing framework
Install your testing framework
Open the Shell tab in your Replit workspace (Tools > Shell). For Node.js/Express.js projects, install Jest and supertest (for HTTP request testing). For Python/Flask projects, install pytest and requests. These are the most widely used testing frameworks for their respective languages and have extensive documentation and community support.
1# For Node.js (Express.js) projects:2npm install --save-dev jest supertest34# For Python (Flask) projects:5pip install pytest requestsExpected result: The testing packages install successfully. For Node.js, jest and supertest appear in package.json under devDependencies. For Python, pytest appears in pip list output.
Configure the test script in package.json
Configure the test script in package.json
For Node.js projects, open package.json and add a test script under the scripts section. This tells npm how to run your tests when you execute 'npm test' in Shell. Set the test environment to 'node' for Jest. For Python projects, you can skip this step since pytest automatically discovers test files that start with test_ or end with _test.py.
1{2 "scripts": {3 "test": "jest --verbose --forceExit",4 "test:watch": "jest --watch",5 "start": "node index.js"6 },7 "jest": {8 "testEnvironment": "node",9 "testMatch": ["**/__tests__/**/*.js", "**/*.test.js"]10 }11}Expected result: Running 'npm test' in Shell invokes Jest with the configured options. If no test files exist yet, Jest reports 'No tests found' rather than an error.
Structure your API for testability
Structure your API for testability
Before writing tests, separate your Express app setup from the server startup. Export the app object from your main file so tests can import it without actually starting the server on a port. This pattern is essential for testing since supertest creates its own temporary server. Create an app.js file for the Express configuration and an index.js file that imports the app and calls listen.
1// app.js - Express configuration (exported for testing)2const express = require('express');3const cors = require('cors');4const app = express();56app.use(cors());7app.use(express.json());89app.get('/api/health', (req, res) => {10 res.json({ status: 'ok' });11});1213app.post('/api/users', (req, res) => {14 const { email, name } = req.body;15 if (!email || !name) {16 return res.status(400).json({ error: 'Email and name are required' });17 }18 res.status(201).json({ id: Date.now(), email, name });19});2021app.get('/api/users/:id', (req, res) => {22 const { id } = req.params;23 if (id === '0') {24 return res.status(404).json({ error: 'User not found' });25 }26 res.json({ id, email: 'test@example.com', name: 'Test User' });27});2829module.exports = app;3031// index.js - Server startup (not imported by tests)32const app = require('./app');33const PORT = process.env.PORT || 3000;34app.listen(PORT, '0.0.0.0', () => {35 console.log(`Server running on port ${PORT}`);36});Expected result: Your app.js exports the Express app object. Your index.js imports it and starts the server. Tests will import from app.js directly.
Write unit tests for your API endpoints
Write unit tests for your API endpoints
Create a test file that imports your app and uses supertest to make HTTP requests against it. Name the file with the .test.js suffix so Jest automatically discovers it. Write tests that cover successful operations, validation errors, and edge cases. Each test should be independent and not rely on the state created by other tests. The RapidDev engineering team recommends testing at minimum: success cases, validation failures, and not-found scenarios for each endpoint.
1// __tests__/api.test.js2const request = require('supertest');3const app = require('../app');45describe('Health Check', () => {6 test('GET /api/health returns 200 with status ok', async () => {7 const response = await request(app).get('/api/health');8 expect(response.status).toBe(200);9 expect(response.body.status).toBe('ok');10 });11});1213describe('User API', () => {14 test('POST /api/users creates a user with valid data', async () => {15 const response = await request(app)16 .post('/api/users')17 .send({ email: 'new@example.com', name: 'New User' });18 expect(response.status).toBe(201);19 expect(response.body).toHaveProperty('id');20 expect(response.body.email).toBe('new@example.com');21 });2223 test('POST /api/users returns 400 when email is missing', async () => {24 const response = await request(app)25 .post('/api/users')26 .send({ name: 'No Email User' });27 expect(response.status).toBe(400);28 expect(response.body.error).toBe('Email and name are required');29 });3031 test('GET /api/users/:id returns user data', async () => {32 const response = await request(app).get('/api/users/123');33 expect(response.status).toBe(200);34 expect(response.body).toHaveProperty('email');35 });3637 test('GET /api/users/0 returns 404 for non-existent user', async () => {38 const response = await request(app).get('/api/users/0');39 expect(response.status).toBe(404);40 expect(response.body.error).toBe('User not found');41 });42});Expected result: Running 'npm test' shows all tests passing with verbose output. Each test name displays with a checkmark and execution time.
Run tests from Shell and interpret results
Run tests from Shell and interpret results
Open Shell and run 'npm test' for Node.js or 'pytest -v' for Python. Jest displays each test with a pass/fail indicator, the test name, and execution time. At the bottom, a summary shows total tests, passed, failed, and duration. If any tests fail, Jest shows the expected value vs the received value with a clear diff. Fix the failing code or test, save, and run again. Use 'npm run test:watch' to automatically re-run tests when files change.
1# Run all tests2npm test34# Run tests in watch mode (re-runs on file changes)5npm run test:watch67# Run a specific test file8npx jest __tests__/api.test.js910# Run tests matching a pattern11npx jest --testPathPattern="user"Expected result: Jest shows a summary of all tests with pass/fail status. Green checkmarks indicate passing tests. Red X marks indicate failures with detailed error output.
Add tests to the deployment build process
Add tests to the deployment build process
To prevent deploying untested code, add the test command to your deployment build step in the .replit file. This runs your test suite during the build phase, and if any test fails, the deployment is blocked. This is a simple but effective way to ensure only tested code reaches production.
1[deployment]2run = ["sh", "-c", "node index.js"]3build = ["sh", "-c", "npm install && npm test && npm run build"]4deploymentTarget = "cloudrun"Expected result: Deploying the app first runs the test suite. If tests fail, the deployment stops and shows the test failure output in the deployment logs.
Complete working example
1const request = require('supertest');2const app = require('../app');34describe('Health Check Endpoint', () => {5 test('GET /api/health returns 200 with status ok', async () => {6 const response = await request(app).get('/api/health');7 expect(response.status).toBe(200);8 expect(response.body).toEqual({ status: 'ok' });9 });10});1112describe('POST /api/users', () => {13 test('creates a user with valid email and name', async () => {14 const userData = { email: 'test@example.com', name: 'Test User' };15 const response = await request(app)16 .post('/api/users')17 .send(userData)18 .set('Content-Type', 'application/json');1920 expect(response.status).toBe(201);21 expect(response.body).toHaveProperty('id');22 expect(response.body.email).toBe(userData.email);23 expect(response.body.name).toBe(userData.name);24 });2526 test('returns 400 when email is missing', async () => {27 const response = await request(app)28 .post('/api/users')29 .send({ name: 'No Email' });3031 expect(response.status).toBe(400);32 expect(response.body).toHaveProperty('error');33 expect(response.body.error).toContain('required');34 });3536 test('returns 400 when name is missing', async () => {37 const response = await request(app)38 .post('/api/users')39 .send({ email: 'test@example.com' });4041 expect(response.status).toBe(400);42 expect(response.body).toHaveProperty('error');43 });4445 test('returns 400 when body is empty', async () => {46 const response = await request(app)47 .post('/api/users')48 .send({});4950 expect(response.status).toBe(400);51 });52});5354describe('GET /api/users/:id', () => {55 test('returns user data for valid ID', async () => {56 const response = await request(app).get('/api/users/123');5758 expect(response.status).toBe(200);59 expect(response.body).toHaveProperty('id');60 expect(response.body).toHaveProperty('email');61 expect(response.body).toHaveProperty('name');62 });6364 test('returns 404 for non-existent user', async () => {65 const response = await request(app).get('/api/users/0');6667 expect(response.status).toBe(404);68 expect(response.body.error).toBe('User not found');69 });70});Common mistakes when running backend unit tests in Replit
Why it's a problem: Importing the server file (index.js) instead of the app file (app.js) in tests, causing 'port already in use' errors
How to avoid: Export the Express app from a separate app.js file and import only that file in tests. The server startup (app.listen) should be in index.js, which tests never import.
Why it's a problem: Tests pass locally but the deployed app fails because environment variables are missing
How to avoid: Mock environment variables in tests using process.env assignments in beforeAll blocks, and verify that deployment secrets match workspace secrets.
Why it's a problem: Jest hangs after tests complete because of open database connections or timers
How to avoid: Add --forceExit to your Jest command. Also add afterAll blocks that close database connections and clear timers.
Why it's a problem: Writing tests that depend on data created by previous tests
How to avoid: Each test should set up its own data and clean up after itself. Use beforeEach to reset state and afterEach to clean up.
Best practices
- Separate your Express app setup from server startup by exporting the app object from a dedicated app.js file
- Install testing packages as devDependencies so they are not bundled in production deployments
- Write tests that cover success cases, validation errors, not-found scenarios, and edge cases for each endpoint
- Use descriptive test names that read as sentences explaining expected behavior
- Keep tests independent so each test can run in isolation without depending on other tests
- Use the --forceExit flag with Jest to prevent hanging from unclosed database connections
- Add the test command to your deployment build step to block deployment of untested code
- Mock external dependencies (databases, third-party APIs) so tests run fast and do not require live services
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
I have an Express.js API in Replit with endpoints for user CRUD operations. Help me set up Jest with supertest to write unit tests. I need tests for POST /api/users (valid data, missing fields), GET /api/users/:id (found, not found), and a health check endpoint. Include the package.json scripts and test file.
Set up Jest testing for this Express.js API. Install jest and supertest as devDependencies. Separate the app configuration from server startup. Create a __tests__/api.test.js file with tests for every endpoint. Add a test script to package.json. Make sure each test covers both success and error cases.
Frequently asked questions
Jest is the most popular choice and works well in Replit. It includes a test runner, assertion library, and mocking tools in one package. Pair it with supertest for HTTP endpoint testing. Mocha and Chai are alternatives but require more configuration.
Yes. Install pytest with 'pip install pytest' in Shell. Create test files prefixed with test_ (like test_api.py). Flask has a built-in test client: use app.test_client() to make requests without starting the server. Run tests with 'pytest -v' in Shell.
Use Jest's jest.mock() to replace your database module with a mock implementation. For example: jest.mock('./db', () => ({ query: jest.fn().mockResolvedValue([{ id: 1 }]) })). This prevents tests from needing a live database connection.
Open handles like database connections, HTTP servers, or timers prevent Jest from exiting. Add --forceExit to your test script as a workaround, and close all connections in afterAll blocks for a proper fix.
Yes. Prompt Agent with: 'Write Jest unit tests for all endpoints in app.js. Include tests for success cases, validation errors, and not-found responses. Create the test file in __tests__/api.test.js.' Agent v4 can generate comprehensive test suites based on your existing API code.
Use 'npx jest --testPathPattern=filename' to run tests from a specific file. Use 'npx jest -t "test name pattern"' to run tests matching a name pattern. In pytest, use 'pytest test_file.py::test_function_name' for a specific test.
For unit tests, mock the database to keep tests fast and independent. For integration tests, use Replit's built-in PostgreSQL with a separate test database. Clear the test database before each test run to ensure clean state.
Yes. The RapidDev engineering team can help design and implement a testing strategy that includes unit tests, integration tests, and end-to-end tests tailored to your Replit project's architecture and deployment pipeline.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation