How to test Node.js: Mocking HTTP request

How to test Node.js: Mocking HTTP request

How to test Node.js: Mocking HTTP request ... Writing tests for an application that relies on external services, say, a RESTful API, is challenging. ✈️✈️✈️✈️✈️

More often than not, an external resource may require authentication, authorization or may have a rate limiting.Hitting an endpoint for a service hosted in a service like AWS as part of testing would also incur extra charges.

This quickly goes out of hand when you are running tests a couple of times a day as a team, as well as part of continous integration. Nock, a HTTP mocking and expectations library for Node.js can be used to avoid this.

Objectives


Table of Contents

  • Objectives
  • Setting Up The Project
  • Creating A Node.js App To Test
  • Testing: The Wrong Way
  • Testing: The Right Way
  • Conclusion

By the end of this post, we will have achieved the following.

  • Written a simple Node.js application that makes a HTTP request to an external endpoint.
  • Write tests for the application
  • Mock the requests in the test.

Setting Up The Project

To get started, create a simple Node.js application by creating an empty folder and running npm init.

mkdir nock-tests
cd nock-tests
npm init

Installing the packages

Next, we will install the following packages for our application and testing environment.

  • Axios - A Promise based HTTP client for the browser and node.js
  • Mocha - A popular Node.js testing framework.
  • Chai - A BDD / TDD assertion library for Node.js
  • Nock - A HTTP mocking and expectations library for Node.js
npm install --save axios
npm install --save-dev mocha chai nock

Setting up tests

Our tests will live inside a test directory, Go ahead and create a test directory and create our first test.

mkdir test
touch test/index.test.js

Our first test should be pretty straightforward. Assert that true is, well, true.

/test/index.test.js

const expect = require('chai').expect;

describe('First test', () => { it('Should assert true to be true', () => { expect(true).to.be.true; }); });

To run our test, we could run the mocha command from our node_modules but that can get annoying. We are instead going to add it as an npm script.

/package.json

{
  "name": "nock-tests",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "node_modules/.bin/mocha"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.16.2"
  },
  "devDependencies": {
    "chai": "^4.0.2",
    "mocha": "^3.4.2",
    "nock": "^9.0.13"
  }
}

At this point, running npm test on your command-line should give you the following result.

$ npm test > [email protected] test /Users/username/projects/nock-tests > mocha First test ✓ Should assert true to be true 1 passing (15ms)

We obviously don't have any tests making requests, or a useful request for that matter, but we will be changing that.

Creating A Node.js App To Test

Let's go ahead and write a simple function that makes a HTTP request to the Github API to get a user by username. Go ahead and create an index.js file in the root and add the following code.

/index.js

const axios = require('axios');

module.exports = { getUser(username) { return axios .get(https://api.github.com/users/${username}) .then(res => res.data) .catch(error => console.log(error)); } };

Testing: The Wrong Way

Our test will assert that the request made returns an object with specific details. Replace the truthy test we created earlier with the following test for our code.

const expect = require('chai').expect;

const getUser = require('../index').getUser;

describe('Get User tests', () => { it('Get a user by username', () => { return getUser('octocat') .then(response => { //expect an object back expect(typeof response).to.equal('object');

    //Test result of name, company and location for the response
    expect(response.name).to.equal('The Octocat')
    expect(response.company).to.equal('GitHub')
    expect(response.location).to.equal('San Francisco')
  });

}); });

Let's break down the test.

  • We import the getUser method from /index.js.
  • We then call the function and assert that we get an object back and that the user's name, company and location match.

This should pass on running the test by actually making a request to the Github API. Let's fix this!

Testing: The Right Way

Nock works by overriding Node's http.request function. Also, it overrides http.ClientRequest too to cover for modules that use it directly.

With Nock, you can specify the HTTP endpoint to mock as well as the response expected from the request in JSON format. The whole idea behind this is that we are not testing the Github API, we are testing our own application. For this reason, we make the assumption that the Github API's response is predictable.

To mock the request, we will import nock into our test and add the request and expected response in the beforeEach method.

/test/index.test.js

const expect = require('chai').expect;
const nock = require('nock');

const getUser = require('../index').getUser; const response = require('./response');

describe('Get User tests', () => { beforeEach(() => { nock('https://api.github.com') .get('/users/octocat') .reply(200, response); });

it('Get a user by username', () => { return getUser('octocat') .then(response => { //expect an object back expect(typeof response).to.equal('object');

    //Test result of name, company and location for the response
    expect(response.name).to.equal('The Octocat')
    expect(response.company).to.equal('GitHub')
    expect(response.location).to.equal('San Francisco')
  });

}); });

The expected response is defined as an export in a separate file.

/test/response.js

module.exports = { login: 'octocat',
  id: 583231,
  avatar_url: 'https://avatars0.githubusercontent.com/u/583231?v=3',
  gravatar_id: '',
  url: 'https://api.github.com/users/octocat',
  html_url: 'https://github.com/octocat',
  followers_url: 'https://api.github.com/users/octocat/followers',
  following_url: 'https://api.github.com/users/octocat/following{/other_user}',
  gists_url: 'https://api.github.com/users/octocat/gists{/gist_id}',
  starred_url: 'https://api.github.com/users/octocat/starred{/owner}{/repo}',
  subscriptions_url: 'https://api.github.com/users/octocat/subscriptions',
  organizations_url: 'https://api.github.com/users/octocat/orgs',
  repos_url: 'https://api.github.com/users/octocat/repos',
  events_url: 'https://api.github.com/users/octocat/events{/privacy}',
  received_events_url: 'https://api.github.com/users/octocat/received_events',
  type: 'User',
  site_admin: false,
  name: 'The Octocat',
  company: 'GitHub',
  blog: 'http://www.github.com/blog',
  location: 'San Francisco',
  email: null,
  hireable: null,
  bio: null,
  public_repos: 7,
  public_gists: 8,
  followers: 1840,
  following: 6,
  created_at: '2011-01-25T18:44:36Z',
  updated_at: '2017-07-06T21:26:58Z' };

To test that this is the actual response expected in the test, try editing one of the fields in the response object and run the test again. The tests should fail.

In my case, I will be changing the name value to Scotch. You should get the error below.


Conclusion

We have only scratched the surface on what you can do with nock. It has a very detailed documentation on how to use it and it is worth exploring. For instance, If you are writing tests that involve error handling, you could mock error responses with an error message.

nock('http://www.google.com')
   .get('/cat-poems')
   .replyWithError('something awful happened');

Happy testing!

========================================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Angular & NodeJS - The MEAN Stack Guide

☞ NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

☞ Docker for Node.js Projects From a Docker Captain

☞ Intro To MySQL With Node.js - Learn To Use MySQL with Node!

☞ Node.js Absolute Beginners Guide - Learn Node From Scratch

☞ React Node FullStack - Social Network from Scratch to Deploy

☞ Selenium WebDriver - JavaScript nodeJS webdriver IO & more!

☞ Complete Next.js with React & Node - Beautiful Portfolio App

☞ Build a Blockchain & Cryptocurrency | Full-Stack Edition

node-js

What's new in Bootstrap 5 and when Bootstrap 5 release date?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Random Password Generator Online

HTML Color Picker online | HEX Color Picker | RGB Color Picker

How to Hire Node.js Developers And How Much Does It Cost?

A Guide to Hire Node.js Developers who can help you create fast and efficient web applications. Also, know how much does it cost to hire Node.js Developers.

Hands on with Node.Js Streams | Examples & Approach

The practical implications of having Streams in Node.js are vast. Nodejs Streams are a great way to handle data chunks and uncomplicate development.

Node.js Performance: Node.js vs Io.js

You may already be aware that Raygun uses Node.JS for our API nodes that receive your precious crash reporting data (we also do node.js crash reporting if you’re interested). We’ve peaked in the past at more than 110,000 requests per second coming...

Node.js Live | Node.js Docker Tutorial | Dockerizing Node.js App|Node.js Training|Edureka

🔥 Node.js Certification Training: https://www.edureka.co/nodejs-certification-training This Edureka video on 'Node.js Docker Tutorial' will help you in learn...

Node JS Complete Course PDF | Node.js

There are some Features that choose Node.js the foremost decision of programming designers.