The Startup
Published in

The Startup

Mock a Library in Jest and Typescript

Get rid of the dependencies with ease

Photo by Fitore F on Unsplash

Importing libraries to extend software capabilities is a productive approach in development. As described in the DRY (Don’t Repeat Yourself) principle, it is not sensible to reinvent the wheels in most cases. Building application with libraries makes much sense. However, in TDD, testing your functions depending on third party libraries could be tricky if it lacks planning.

Let’s warm up by writing a simple Typescript function to hit an external REST service SpaceX-API and retrieve the response’s name attribute. We will leverage the popular Rest library ‘Axios’ and ride on the get() function to implement it. Sitting on a giant’s shoulder, isn’t it?

It’s time to roll your sleeves up. Are you ready?

Setup

If you are hungry for the source code, check out here.

In a brand new project, install dependencies for development.

$ yarn add -D typescript jest @types/jest ts-jest

Create Typescript config tsconfig.json

$ tsc --init

Create Jest config jest.config.js

$ yarn ts-jest config:init

Add roots property in jest.config.js.

roots: [
'<rootDir>/src'
],

Lastly, create an empty src folder under the project root directory.

Wanna know more about the setup? Head over to Jest with Typescript.

Scenario

We’re gonna write a module that uses Axios to make an HTTP Get request to free SpaceX-API.

Let’s hit the endpoint to see what the outcome will be.

GET https://api.spacexdata.com/v4/launches/latest

Our goal is to retrieve the name attribute under the root in the response. The name value is “Turksat 5A” when this post is written. This value changes when there is a new flight. It is a perfect example to demonstrate the unit test will be flaky if it relies on real endpoint. We will discuss about it very soon.

{
...
flight_number: 113,
name: “Turksat 5A”,
...
}

Simple enough?

Unit Test

We’ll get started with a test case under the ‘src’ folder. In the test, we will call a fetch() function that will be implemented very soon (line 3). The result will be tested against our expected ‘Turksat 5A’ (line 4).

Next, we’ll implement our fetch() in data.ts and return an empty string.

Compile the module, generate the build, and run the test.

$ tsc — outDir build && npx jest

It failed as expected.

➜  mock-library-jest-typescript git:(main) ✗ npx jest          
FAIL src/data.test.ts
✕ returns name successfully (4 ms)
● returns name successfullyexpect(received).toBe(expected) // Object.is equalityExpected: "Turksat 5A"
Received: {}
3 | it("returns name successfully", async () => {
4 | const actual = fetch();
> 5 | expect(actual).toBe("Turksat 5A");
| ^
6 | });
7 |
at src/data.test.ts:5:18
at step (src/data.test.ts:33:23)
at Object.next (src/data.test.ts:14:53)
at src/data.test.ts:8:71
at Object.<anonymous>.__awaiter (src/data.test.ts:4:12)
at Object.<anonymous> (src/data.test.ts:3:33)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.551 s, estimated 2 s
Ran all test suites.

Don’t panic.

Let’s update data.ts with real functionality.

Implementation

Install Axios.

$ yarn add axios

Update fetch() to return a promise of Axios response.

Re-run the test and it passes. Hurray! 🚀

➜  mock-library-jest-typescript git:(main) ✗ npx jest
PASS src/data.test.ts
✓ returns name successfully (205 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.021 s
Ran all test suites.

Why mocking?

The nightly build is so reassuring until the SpaceX-API is down for maintenance on the other night. You were awake by a Slack notification warning of a failure in the CI pipeline.

Imagine you are in an incredible tech team delivering with CI/CD timely. The data module is part of the data acquisition services in the company. The unit test we have just written depends on the external API. In other words, the test results are tightly coupled with the availability of the SpaceX-API. It is crucial to be notified when the endpoint was not working. However, it is not the concern of the unit test. Our unit test aims to ensure the fetch() works properly against the contract of the endpoint. The test verifies the implementation can handle different scenarios such as bad requests, successful calls, service unavailable, etc., that are defined in the contract.

To avoid an unnecessary failing in the tests, we’re gonna get rid of the dependencies by mocking.

Jest mock function

Jest provides a mock function for replacing a Node module during the test. In the test file data.spec.ts, import the Axios library and instruct Jest to mock it up.

import axios, { AxiosResponse } from "axios";jest.mock("axios");

Create an object of type of mocked Axios.

const mockedAxios = axios as jest.Mocked<typeof axios>;

Now you have full control of the mocked Axios. Of course, we want to make our desired output for the get function. Here we go.

It’s time to wire up the get function of our mocked Axios and our desired output. As the get function is asynchronous, we use mockResolvedValue for a success case.

mockedAxios.get.mockResolvedValue(mockedResponse);

In our assertion, we first make sure the mocked axios.get() has not been called before we trigger the fetch() function.

After we call the fetch(), we presume the mocked axios.get() in invoked with the toHaveBeenCalled() function.

expect(axios.get).not.toHaveBeenCalled();const actual = await fetch();expect(axios.get).toHaveBeenCalled();

Finally, we assert the response object contains our mocked value.

expect(actual.data.name).toEqual(“Henry”);

Run our test again.

It passes! Meaning the test satisfied the contract.

Here is the key takeaway of this post.

  1. Remove the third parties dependencies from the unit tests.
  2. By mocking functions with Jest, you can test your implementation based on contract rather than the real services.

The complete source code can be found here.

Happy coding and sleep tight!

If you like testing with Jest in Typescript, you might be interested in the other two posts in the Jest series. Cheers.

  1. Jest with Typescript
  2. Mock DynamoDB in Typescript

--

--

--

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +756K followers.

Recommended from Medium

Containers and Presentationals: A More Modular SPA Architecture

Creating, Testing and Publishing a VSCode Extension

CONTROL STATEMENTS IN JAVA.

Electron Extensions — Part I

Top 10 popular Javascript projects on Github

Microservices vs ArchieJs Modules — comparison

Worker Threads In Node.js

Face Tracking With JavaScript on the Browser (Mobile or Desktop)

Analyzing two faces

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Anthony Ng

Anthony Ng

Software Engineer

More from Medium

Type-safety while incrementally migrating a large app from Flow to TypeScript

Declarative Components, Imperative Objects

Type safety with JSDocs

Customizable confirmation dialog in React.js using Hooks, Context API, and TypeScript