A Step-by-Step Guide to Unit Testing Node.js Apps with Jest

Testing forms a crucial part of robust and resilient software development. As you build more complex Node.js applications – whether user-facing web apps, backends, APIs, or microservices – comprehensive testing is essential to detecting bugs early and maintaining quality over time.

In this detailed walkthrough, I’ll be sharing my insight as a professional testing expert with over 10+ years experience helping large enterprises adopt test-driven development. Here’s what we’ll be covering together:

  • Why Unit Testing Node.js Apps Matters More Than Ever – The growth of Node.js in production means more testing surface area
  • Choosing Jest as Your Unit Testing Framework – Why Jest’s simplicity, speed, and snapshots fit Node.js apps perfectly
  • Installing & Configuring Jest from Scratch – Step-by-step setup guide with code examples
  • Writing Your First Unit Tests with Jest – Testing a simple math helper module hands-on
  • Expanding Tests Using Jest Features – Mocking, snapshots, organization tips and more
  • Automating Jest Unit Tests in CI Pipelines – GitHub Actions integration to run tests pre-deploy

Let’s get started understanding more about unit testing and why it offers a compelling ROI.

The Growing Need for Testing Node.js Applications

As Node.js usage proliferates across organizations building web and mobile apps, adoption of comprehensive testing methodologies becomes increasingly crucial.

Node.js offers high scalability and real-time performance making it well-suited for modern applications. However, these same benefits lead to increased complexity internally – and like any complex system – a higher chance of latent software defects.

Catching bugs in production is painful and costly. This is why test-driven development focusing on automated checks is key to preventing issues before they impact customers.

According to the 2021 Node.js User Survey Report:

  • 83% of Node.js users employ backend applications and services built with Node
  • 60% use Node.js for front-end web applications
  • 49% use Node for mobile back-end services

With Node.js serving so many critical production use cases, comprehensive testing is no longer optional.

Additionally, trends like microservices and serverless architectures break down apps into independently deployable components. While this enables greater agility through DevOps practices, it also increases the testing surface area substantially.

Each microservice ideally needs its own test suite protecting against regressions.

This is where a reliable unit testing foundation becomes essential – catching bugs at the lowest level early in development.

Let’s explore now why Jest is the ideal choice for testing Node.js apps.

Choosing Jest As Your Unit Testing Framework

The JavaScript ecosystem offers many capable tester frameworks including favorites like Mocha, Jasmine and Tape. However, Jest stands out as a modern solution specifically tailored for testing Node.js applications.

Created by Facebook focused on simplicity, performance, and support for monorepos/large codebases – it aligns nicely with Node’s design goals.

Key Reasons Why Jest Fits Node.js Apps Perfectly:

🔥 Very Fast Test Execution – Jest parallelizes test runs efficiently utilizing all CPU cores. Critical for large projects.

🎯 Built-in Code Coverage – See exactly which lines/branches execute. No extra libraries needed!

✅ Snapshot Testing – Catch unintended UI changes easily. Great for React node apps.

🧩 Easy Mocking – Stub dependencies seamlessly like databases, HTTP etc. Avoid slow integration tests.

💡 Simple Setup – Jest works out the box. Just install and write tests!

📸 Interactive CLI – Get test results directly in terminal without switching contexts.

With Jest offering many compelling benefits, it continues gaining popularity testing Node.js and JavaScript projects across startups and large enterprises alike – including companies like Netflix, Facebook, Spotify, and more.

Next let’s go hands-on installing Jest and writing our first tests!

Step 1 – Installing Jest for Testing Node.js

Ensure you have Node.js >= 12 installed on your system.

Initialize a new project:

$ mkdir my-app 
$ cd my-app
$ npm init -y # accept defaults 

Install Jest:

$ npm install --save-dev jest

This installs the jest package locally as a development dependency, as npm best practice avoids bloating production dependencies.

Let’s verify Jest installed correctly by printing the version:

$ npx jest --version
# v27.5.1

Looking good!

Step 2 – Configuring Jest with Node.js

While Jest works out of the box, we can create a basic config file to specify we want to run tests in a Node.js environment:

// jest.config.js

module.exports = {
  testEnvironment: "node" 
};

This ensures tests use a Node.js runtime instead of JSDOM, Jest‘s browser-like testing environment.

With that setup, we‘re ready to starting writing actual app code and tests!

Step 3 – Creating the Application to Test

Under the hood, Jest uses Jasmine – so you may see references to Jasmine docs around assertions and matchers.

Let‘s make a simple math.js module exporting helper functions:

// math.js
function sum(a, b) {
  return a + b;
}

function prod(a, b) {
  return a * b;  
}

module.exports = {sum, prod};

And an app.js file showing usage:

// app.js
const math = require(‘./math‘);

console.log(math.sum(5, 3)); // 8 
console.log(math.prod(3, 7)); // 21

This gives us a toy app to demonstrate testing!

Step 4 – Writing Your First Jest Unit Tests

Unit tests typically reside within a __tests__ directory adjacent to the target module‘s source code. This keeps related code and tests collocated together.

Let‘s create __tests__/math.test.js:

const math = require(‘../math‘);

test(‘adds numbers‘, () => {
  expect(math.sum(1, 2)).toBe(3);   
});

test(‘multiplies numbers‘, () => { 
  expect(math.prod(3, 5)).toBe(15);
}); 

Writing tests with Jest looks very clean and readable owing to its developer experience focus.

  • test() defines a test case
  • expect() makes an assertion
  • .toBe() compares expected vs actual

Let‘s run our tests from the command line:

$ npm test

And you should see successful output like below!

 PASS  __tests__/math.test.js
  ✓ adds numbers (8 ms)
  ✓ multiplies numbers

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 math.js  |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total

Notice the coverage metrics are also reported about the percentage of code tested!

Step 5 – Expanding Tests with Jest Functionality

Now that we have Jest setup and passing tests, let‘s explore some more advanced functionality useful for testing Node.js apps!

Organizing Test Suites with describe() Blocks

The describe(name, callback) method lets us group together related test cases:

const math = require(‘../math‘);

describe(‘math‘, () => {

  test(‘adds numbers‘, () => {  
    // Test 1
  });

  test(‘multiply numbers‘, () => {
   // Test 2   
  });

});

This also neatly categorizes test suites within reporter output.

Mocking Modules with jest.mock()

Say our math module relied on an external stats helper for operations:

// math.js
const stats = require(‘./stats‘); 

function mean(arr) {
  const sum = arr.reduce((a, b) => a + b);  
  return stats.divide(sum, arr.length);
} 

We can mock it out completely avoiding slow integration testing:

jest.mock(‘./stats.js‘); 

test(‘calculates mean‘, () => {
  expect(math.mean([1, 2, 3])).toBe(2); 
}); 

Mocks and spies give you fine-grained control isolating test cases.

Snapshot Testing User Interfaces

Jest snapshots capture rendered UI markup for comparison against changes:

// UI component
function Button({text}) {
  return <button>{text}</button>;
}

test(‘matches previous Button snapshot‘, () => {

  const button = renderer.create(<Button text="Save"/>);  
  expect(button).toMatchSnapshot();

});

Snapshots help ensure UI doesn‘t unexpectedly regress as code evolves across commits.

This covers some helpful starter concepts for testing Node.js with Jest! Now let‘s look at executing tests automatically whenever code changes.

Step 6 – Automating Tests in CI Pipelines

To enforce tests run pre-deployment, integrate Jest with continuous integration (CI) workflows like GitHub Actions:

# .github/workflows/tests.yml

on:
  push: 
    branches: [main]

jobs:

  test:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3

    - name: Install dependencies
      run: npm ci

    - name: Run Jest tests
      run: npm test

Now on every Git push, GitHub will:

  1. Checkout code
  2. Install npm packages
  3. Execute Jest tests automatically

Failing jobs block merges so developers address issues before reaching production.

Similarly you could publish only versions with passing tests to npm or trigger deployment tasks.


And there you have it! You should now feel right at home using the Jest testing framework for unit testing Node.js applications following best practices.

Let me know if you have any other questions as you being adopting TDD workflows for your JavaScript projects!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts