Sheriff Quadri Software engineer

API mock testing with Nock for Node.js apps

3 min read 1080

API mock testing with Nock

When testing individual units of our application, our tests must be isolated. This helps to increase the speed of our tests and reduce the number of dependencies required to run the tests.

However, there will be some parts of the application that rely on data from external systems to run, such as databases, file systems, or external APIs. To test these parts, we make use of a mock object.

What is a mock?

A mock is a method or object that mimics the behavior of an external system in a controlled way. Mocks are created in a manner that stimulates the behavior of an external system — they are called and respond in the same manner as the external system. Examples of popular mocking libraries include:

In this guide, we will learn how to perform API mock testing in a simple Node.js application using Nock.

What is Nock?

Nock is an HTTP server mocking and expectations library for Node.js. Nock works by overriding the http.request and http.ClientRequest functions, intercepting all requests made to a specified URL and returning specified responses that mimic the data that the real URL would return.

Using Nock as a mock server, we can easily perform tests on modules that make HTTP requests to an external object like a URL without actually making any requests to the URL. This makes our testing process faster and more efficient.

Nock installation and configuration

Install Nock with the following command.

$ npm install --save-dev nock

A simple Nock mocking object looks something like this:

import nock from "nock";
const scope = nock("https://api.weatherapi.com/v1")
     .get(``)
     .reply(200, {
       data: {
         id: 1,
         title: "The weather is nice",
         completed: true
       }
     });

This mocking object intercepts a GET request to the specified URL — we’re using https://api.weatherapi.com/v1 as an example — and replies with the status 200 and a set of values.

Mock testing with Nock

Let’s say we want to test a function that makes an HTTP request to a URL and returns the data provided by the URL.



const getData = async () => {
   const res = await axios.get('https://api.example.com');

   const data = res.data;
   return data;
}

A simple Jest test for the getData() function would look like this:

describe('expectedData', () => {

   it("checks if API returns expected data", async () => {
       nock('https://api.example.com')
           .get('/')
           .reply(200, {
               data: {
                   id: 1,
                   title: "The weather is nice",
                   completed: true
               }
           });
       const results = await getData();
       expect(results.data.title).toEqual("The weather is nice");
   });
});

Whenever the test is run, it creates the mocking server with Nock and also makes a call to the getData() function. The mocking server then intercepts the HTTP request made by the getData() function to the 'https://api.example.com' URL, and sends to it the specified reply.

The testing framework then checks if the data returned by the getData() function matches the data given to it by the mocking server. Our test is fully isolated, as we are not making a real HTTP request to our example URL, but rather intercepting the request with Nock and returning a specified dataset.

Specifying requests in Nock

Nock allows us to mock requests to all HTTP verbs (GET, PUT, POST, PATCH, DELETE, etc.) and also provides support for calls made on either HTTP or HTTPS.

A simple POST request made over HTTP looks like this:

scope = nock('http://api.example.com')
                .post('/users', {
                  username: 'user',
                  email: '[email protected]'
                })
                .reply(201);

In the above code snippet, we create a mock object that intercepts a POST request to the specified hostname (http://api.example.com/), with a request body of { username: 'user', email: '[email protected]'}, and it returns a 201 status code.

Nock also makes it very easy to add query parameters to our requests:


More great articles from LogRocket:


scope = nock('http://api.example.com')
                .get('/users')
                .query({username: 'sugar', email: '[email protected]'})
                .reply(200);

Nock will intercept an HTTP request with the specified query and return a status code of 200.

Specifying request and response headers

Nock makes it easy to append both request and response headers. This is useful if you want to test for the presence of specific headers in your HTTP requests or responses.

Simple request headers can be added like this:

const scope = nock('http://api.example.com', {
  reqheaders: {
    authorization: 'Basic Auth',
    'Content-Type': 'application/json'
  },
})
  .get('')
  .reply(200)

In the above code snippet, we added authorization and Content-Type headers to a simple GET request that returns a 200 status code. Nock will intercept HTTP calls to the specified URL with the added request headers, and return the 200 status code.

You can also add expected headers to your replies.

const scope = nock('http://api.example.com')
  .get('')
  .reply(200, { license: 'MIT' })

This code sample returns a status code of 200 and a header of { license: 'MIT' } when it intercepts a GET request to http://api.example.com.

Specifying custom replies

Nock provides support for creating custom replies. You can specify a reply body like this:

scope = nock('http://api.example.com')
                .get('/users')
                .reply(200, {
              data:{
                    id: 1,
                    username: 'user',
                    email: '[email protected]'
                  }
                });

This Nock object returns a status code of 200 and a body of (data:{id: 1, username: 'user', email: '[email protected]'}) when it intercepts a GET request to http://api.example.com/users.

You can return error replies from the mocking server.

scope = nock('http://api.example.com')
                .get('/users')
                .replyWithError({
              message: 'The server is down, ERROR'
                });

Request chaining

You will also have scenarios where you need to mock HTTP requests that depend on the responses of previous requests. Nock makes it very easy to add chaining behavior to the mocking server.

const scope = nock('http://api.example.com')
  .delete('/users/1')
  .reply(200)
  .get('/users/1')
  .reply(404)
  .post(‘users’, , {
    username: 'user',
    email: '[email protected]',
  })
  .reply(200, ‘user created’)
  .get('/users')
  .reply(200, {
    _id: '1',
    username: 'user',
    email: '[email protected]',
  })

This mock object comprises four different requests; requests down the chain will only start when the request on top of it is complete.

Conclusion

In this article, we learned how to perform API mock testing in Node.js applications with Nock. Nock is an amazing library that allows you to test our modules and functions that make HTTP requests in isolation.

Using a Nock mocking server also helps to improve our software development process by making our engineering teams more efficient. Visit the Nock GitHub page to learn more amazing ways you can use Nock for better API mock testing.

200’s only Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. .
Sheriff Quadri Software engineer

Leave a Reply