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.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
How to Remove the Last Character from a String in JavaScript
How to Remove the Last Character from a String in JavaScript

What is the Difference Between Null and Undefined in JavaScript
What is the Difference Between Null and Undefined in JavaScript

How to Hide Scrollbar with CSS
How to Hide Scrollbar with CSS

How to Use JavaScript setTimeout()
How to Use JavaScript setTimeout()

Answers by CoreUI Core Team