Overview
The Node.js service uses Jest as the testing framework with Supertest for HTTP endpoint testing and Sequelize for database management. Tests run against a test database that is synchronized before each test suite.
Jest Configuration
jest.config.js
module . exports = {
testEnvironment: "node" ,
testMatch: [ "**/tests/**/*.test.js" ],
setupFilesAfterEnv: [],
verbose: true ,
};
Configuration Details :
testEnvironment : Node.js environment (not browser)
testMatch : Finds all files ending in .test.js in the tests/ directory
verbose : Displays individual test results
Test Setup (setup.js)
Global setup and teardown for all tests:
const { sequelize } = require ( "../src/models" );
beforeAll ( async () => {
await sequelize . sync ({ force: true });
});
afterAll ( async () => {
await sequelize . close ();
});
Setup Process :
beforeAll: Synchronizes database schema (drops and recreates tables)
afterAll: Closes database connection
Test Files
auth.test.js - Authentication Tests
Tests for user registration and login functionality.
Setup :
beforeAll ( async () => {
await sequelize . sync ({ force: true });
});
afterAll ( async () => {
await sequelize . close ();
});
beforeEach ( async () => {
await User . destroy ({ where: {} }); // Clear users before each test
});
Example Test :
describe ( "User Registration" , () => {
test ( "should register user with valid data" , async () => {
const res = await request ( app )
. post ( "/api/auth/register" )
. send ({
email: "[email protected] " ,
password: "password123" ,
name: "Test User" ,
});
expect ( res . statusCode ). toBe ( 201 );
expect ( res . body . user . email ). toBe ( "[email protected] " );
expect ( res . body . token ). toBeDefined ();
});
});
Coverage :
Registration with valid data
Email validation (plus addressing: [email protected] )
Email validation (dots: [email protected] )
Missing password rejection
Duplicate email prevention (409 status)
Login with valid credentials
Invalid password rejection (401 status)
Non-existent email handling
users.test.js - User Profile Tests
Tests for user profile retrieval and management.
Setup with Authentication :
let authToken ;
let testUserId ;
beforeAll ( async () => {
await sequelize . sync ({ force: true });
// Register a test user
const res = await request ( app )
. post ( "/api/auth/register" )
. send ({
email: "[email protected] " ,
password: "password123" ,
name: "User Test" ,
});
authToken = res . body . token ;
testUserId = res . body . user . id ;
});
Example Test :
describe ( "GET /api/users/:id" , () => {
test ( "should return user by ID" , async () => {
const res = await request ( app )
. get ( `/api/users/ ${ testUserId } ` )
. set ( "Authorization" , `Bearer ${ authToken } ` );
expect ( res . statusCode ). toBe ( 200 );
expect ( res . body . user . email ). toBe ( "[email protected] " );
});
test ( "should return 404 for non-existent user" , async () => {
const res = await request ( app )
. get ( "/api/users/99999" )
. set ( "Authorization" , `Bearer ${ authToken } ` );
expect ( res . statusCode ). toBe ( 404 );
expect ( res . body . error ). toBeDefined ();
});
});
Coverage :
User retrieval by ID
404 for non-existent users
Profile retrieval for users without profile data
Profile retrieval after profile is set
Profile update functionality
Authentication requirement
products.test.js - Product Management Tests
Tests for product listing, pagination, search, and creation.
Setup with Seed Data :
beforeAll ( async () => {
await sequelize . sync ({ force: true });
// Get auth token
const res = await request ( app )
. post ( "/api/auth/register" )
. send ({
email: "[email protected] " ,
password: "password123" ,
name: "Product Test" ,
});
authToken = res . body . token ;
// Seed 25 products
const products = [];
for ( let i = 1 ; i <= 25 ; i ++ ) {
products . push ({
name: `Product ${ i } ` ,
description: `Description for product ${ i } ` ,
price: ( i * 10.99 ). toFixed ( 2 ),
stock: i * 5 ,
category: i % 3 === 0 ? "electronics" : i % 2 === 0 ? "clothing" : "books" ,
});
}
await Product . bulkCreate ( products );
});
Example Test :
describe ( "GET /api/products" , () => {
test ( "should return first page correctly" , async () => {
const res = await request ( app ). get ( "/api/products?page=1&limit=5" );
expect ( res . statusCode ). toBe ( 200 );
expect ( res . body . products . length ). toBe ( 5 );
expect ( res . body . page ). toBe ( 1 );
expect ( res . body . totalPages ). toBe ( 5 );
});
test ( "should return correct products on page 2" , async () => {
const page1 = await request ( app ). get ( "/api/products?page=1&limit=5" );
const page2 = await request ( app ). get ( "/api/products?page=2&limit=5" );
expect ( page2 . statusCode ). toBe ( 200 );
// Pages should not overlap
const page1Ids = page1 . body . products . map (( p ) => p . id );
const page2Ids = page2 . body . products . map (( p ) => p . id );
const overlap = page1Ids . filter (( id ) => page2Ids . includes ( id ));
expect ( overlap . length ). toBe ( 0 );
});
});
Coverage :
Product listing with default pagination
First page retrieval
Page 2 retrieval (no overlap verification)
Product search by name
400 error for missing search query
Product retrieval by ID
404 for non-existent products
Product creation (authenticated)
Validation for missing required fields
401 for unauthenticated creation attempts
Unit tests for formatting and pagination utilities.
Example Tests :
const { formatUserResponse , formatProductResponse , paginate } = require ( "../src/utils/formatters" );
describe ( "paginate" , () => {
test ( "page 1 should have offset 0" , () => {
const result = paginate ( 1 , 10 );
expect ( result . offset ). toBe ( 0 );
expect ( result . limit ). toBe ( 10 );
expect ( result . page ). toBe ( 1 );
});
test ( "page 2 should have offset equal to limit" , () => {
const result = paginate ( 2 , 10 );
expect ( result . offset ). toBe ( 10 );
expect ( result . limit ). toBe ( 10 );
});
test ( "should default to page 1 and limit 10" , () => {
const result = paginate ( undefined , undefined );
expect ( result . page ). toBe ( 1 );
expect ( result . limit ). toBe ( 10 );
expect ( result . offset ). toBe ( 0 );
});
});
Coverage :
Pagination offset calculation
Default pagination values
User response formatting with profile
User response formatting without profile
Product response formatting
Price type conversion (string to number)
Running Node.js Tests
Install dependencies
cd node-service
npm install
Run all tests
This runs: jest --verbose
Run tests in watch mode
Automatically reruns tests when files change.
Run specific test file
npm test -- tests/auth.test.js
Run tests with coverage
Generates coverage report showing tested code percentage.
Test Output Example
PASS tests/auth.test.js
User Registration
✓ should register user with valid data (234ms)
✓ should accept valid email with plus addressing (89ms)
✓ should accept valid email with dots (87ms)
✓ should reject registration without password (45ms)
✓ should reject duplicate email (123ms)
User Login
✓ should login with valid credentials (156ms)
✓ should reject invalid password (98ms)
✓ should reject non-existent email (67ms)
PASS tests/users.test.js
GET /api/users/:id
✓ should return user by ID (123ms)
✓ should return 404 for non-existent user (45ms)
PASS tests/products.test.js
GET /api/products
✓ should list products with default pagination (156ms)
✓ should return first page correctly (134ms)
Test Suites: 4 passed, 4 total
Tests: 28 passed, 28 total
Snapshots: 0 total
Time: 4.567 s
Common Jest Matchers
expect ( value ). toBe ( expected ); // Strict equality (===)
expect ( value ). toEqual ( expected ); // Deep equality
expect ( value ). toBeDefined (); // Not undefined
expect ( value ). toBeNull (); // Null value
expect ( value ). toBeGreaterThan ( 3 );
expect ( value ). toBeLessThan ( 10 );
expect ( value ). toBeGreaterThanOrEqual ( 5 );
expect ( value ). toBeLessThanOrEqual ( 5 );
expect ( string ). toContain ( "substring" );
expect ( string ). toMatch ( /regex/ );
expect ( array ). toHaveLength ( 5 );
expect ( array ). toContain ( item );
Best Practices
Always clean up database state in beforeEach or afterEach hooks to ensure test isolation.
Use descriptive test names that read like documentation: "should return 404 for non-existent user"
Supertest automatically handles server lifecycle - no need to manually start/stop the Express server.
Debugging Tests
To debug a specific test:
# Run with Node debugger
node --inspect-brk node_modules/.bin/jest tests/auth.test.js
# Run only one test (add .only)
test .only ( "should register user" , async () => {
// This test will run alone
});