How to test Node.js apps with Jest
Testing Node.js applications ensures code reliability and prevents regressions as your codebase grows and evolves. With over 12 years of Node.js development experience since 2014 and as the creator of CoreUI, I’ve written comprehensive test suites for production backends. Jest is a modern testing framework that provides fast test execution, built-in mocking, and excellent error messages out of the box. This approach creates maintainable test suites with minimal configuration.
Use Jest to test Node.js applications with automatic mocking, parallel execution, and coverage reporting.
Install Jest:
npm install --save-dev jest
Configure package.json:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "node",
"coveragePathIgnorePatterns": ["/node_modules/"],
"testMatch": ["**/__tests__/**/*.js", "**/?(*.)+(spec|test).js"]
}
}
Example tests:
// user.service.js
class UserService {
constructor(database) {
this.db = database
}
async getUser(id) {
const user = await this.db.findById(id)
if (!user) {
throw new Error('User not found')
}
return user
}
isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
calculateAge(birthDate) {
const today = new Date()
const birth = new Date(birthDate)
let age = today.getFullYear() - birth.getFullYear()
const monthDiff = today.getMonth() - birth.getMonth()
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
age--
}
return age
}
}
module.exports = UserService
// user.service.test.js
const UserService = require('./user.service')
describe('UserService', () => {
let service
let mockDatabase
beforeEach(() => {
mockDatabase = {
findById: jest.fn()
}
service = new UserService(mockDatabase)
})
afterEach(() => {
jest.clearAllMocks()
})
describe('getUser', () => {
it('should return user when found', async () => {
const mockUser = { id: 1, name: 'John' }
mockDatabase.findById.mockResolvedValue(mockUser)
const result = await service.getUser(1)
expect(result).toEqual(mockUser)
expect(mockDatabase.findById).toHaveBeenCalledWith(1)
expect(mockDatabase.findById).toHaveBeenCalledTimes(1)
})
it('should throw error when user not found', async () => {
mockDatabase.findById.mockResolvedValue(null)
await expect(service.getUser(1)).rejects.toThrow('User not found')
})
})
describe('isValidEmail', () => {
it('should validate correct email', () => {
expect(service.isValidEmail('[email protected]')).toBe(true)
})
it('should reject invalid email', () => {
expect(service.isValidEmail('invalid-email')).toBe(false)
expect(service.isValidEmail('')).toBe(false)
expect(service.isValidEmail('test@')).toBe(false)
})
})
describe('calculateAge', () => {
it('should calculate age correctly', () => {
const birthDate = new Date('1990-01-01')
const age = service.calculateAge(birthDate)
expect(age).toBeGreaterThan(30)
})
it('should handle birthday not yet occurred this year', () => {
const today = new Date()
const thisYear = today.getFullYear()
const futureMonth = today.getMonth() + 2
const birthDate = new Date(thisYear - 25, futureMonth, 1)
const age = service.calculateAge(birthDate)
expect(age).toBe(24)
})
})
})
Testing async code:
it('should handle async operations', async () => {
const result = await fetchData()
expect(result).toBeDefined()
})
it('should use promises', () => {
return fetchData().then(data => {
expect(data).toBeDefined()
})
})
it('should handle async/await errors', async () => {
await expect(failingFunction()).rejects.toThrow('Error message')
})
Mock modules:
jest.mock('./database')
const Database = require('./database')
Database.mockImplementation(() => ({
findById: jest.fn().mockResolvedValue({ id: 1 })
}))
Best Practice Note
Jest runs tests in parallel by default for speed. Use describe blocks to group related tests. Use beforeEach for setup and afterEach for cleanup. Mock external dependencies to isolate what you’re testing. Jest’s coverage reports show which lines need testing. Use --watch mode during development for instant feedback. Snapshot testing is useful for API responses and data structures. This is how we test CoreUI backend services—comprehensive Jest suites with high coverage, fast execution, and clear test organization for maintainable, reliable APIs.



