Using a Mock Object in Tests Using Sync Request

Table of Contents

Challenge

A current project for me at work is the RPA Lint app. It is a Node.js app that helps us develop better Robotic Process Automation (RPA) workflows. We use UiPath as our RPA platform, and UiPath projects contains a collection of XAML files. Each XAML file contains the code for a component of the automation.

XAML is an XML-based language. This means I can use XPath queries to find elements and attributes that I want to check. For example, checking that each argument in a workflow has an annotation.

This week I added a check for outdated project dependencies. The app does this by parsing this UiPath project.json file and building a list of packages. Using this list I lookup package information from the official NuGet repository. If the version number of the specified package doesn't match the latest version, the app reports an error.

The app uses the sync-request1 package. I use it to retrieve the JSON data from the NuGet repository. Using the real library and live API calls caused two main challenges:

  1. The time taken to run the unit test was very long.
  2. Using a live API for testing meant my tests were no longer deterministic.

To solve these challenges, I needed a way to use a mock sync-request object.

The aim of the object is to:

  1. Replicate the function calls that I'm using with the real sync-request object.
  2. Return prepared JSON based on the URL supplied.

There are many different ways of achieving the same thing when writing code. This is especially true for JavaScript and the Node.js ecosystem. The solution I write about here is the solution that works for me. I use the Mocha framework for the unit tests for the app.

Solution

The solution is divided into the following three parts.

Mock Object

The first part of the solution is a mock object. It replicates the functions that I use with the real sync-request package. The object is called MyGetApiMock.js

Replace the real sync-request with the mock object

The Sinon.js package is a development dependency of the app. It is used to replace the real sync-request with the mock object.

First import my mock object, the sinon package and the real sync-request package.

import { MyGetApiMock } from "../../app/mocks/MyGetApiMock.js";
import sinon from "sinon";
import * as request from "sync-request";

Then in the test dynamically replace the sync-request package with the mock object.

// Replace the real sync-request with a mock function.
sinon.stub( request, "default" ).callsFake( function( verb, url ) {
  return new MyGetApiMock( verb, url );
} );

// Test my class which doesn't realise the sync-request is a mock and
// not the real thing

// Restore the proper sync-request.
request.default.restore();

Benefits

Using this approach means I can dynamically replace the functionality provided by the sync-request package with my mock object. This means my tests complete faster, and do not rely on live calls to the NuGet API. It means that my tests are deterministic as they use the same test artefacts each time.


  1. I am well aware that many in the JavaScript community have strong feelings about using synchronous code. This approach works for my use case, don't @ me.