How to mock TypeScript interfaces with Jest

Senior Full Stack Developer in the ☁️
Creator of supereffective.gg, a Pokémon community running on Next.js and @vercel
Search for a command to run...

Senior Full Stack Developer in the ☁️
Creator of supereffective.gg, a Pokémon community running on Next.js and @vercel
No comments yet. Be the first to comment.
Articles about frontend and backend developemnt in JavaScript, TypeScript, NodeJS, ReactJS, ExpressJS, Babel, NextJS, etc...
How to make React Router work with a location-based state. A beginner's guide with real-world use cases and troubleshooting.
In this case, using the scroll event offers more flexibility than using the IntersectionObserver API
Feeling the web developer fatigue? You are not alone!

The IntersectionObserver is a powerful API for tracking elements' visibility within the viewport. Many modern web applications use it to implement lazy loading, infinite scrolling, or other interactions that depend on an element's visibility. When it...
The addon system has been completely rewritten, so now you can also provide your own or turn off default ones. The UI also looks much better, especially on mobile. The styles have been reworked so the look and feel are more similar to StoryBook's UI....

Lightweight drop-in app for rapid React component development and visualisation

Last week I was creating a NodeJS + ExpressJS app in TypeScript and I was wondering how to apply the Onion Architecture successfully. In practice that was not a problem (I will write an article about it soon) until the moment of testing.
There is little to no documentation about how to mock TypeScript interfaces in Jest and what I found was most of the time misleading or not what I was looking for.
First I used jest-mock-extended but I was not very convinced and I ended up playing around with jest until I came up with a working solution.
I think that this could be applied to both NodeJS and browser JS apps. First, you obviously need jest and ts-jest as devDependencies.
Now let's say I have this code under src/DomainModel/Reply and I want to test a class called ReplyService, mocking its dependencies.
src/DomainModel/Reply/ReplyInterface.js
export interface ReplyInterface {
text: string;
}
src/DomainModel/Reply/ReplyRepositoryInterface.js
import {ReplyInterface} from "./ReplyInterface";
export interface ReplyRepositoryInterface {
findOneByIntent: (intentName: string) => Promise<ReplyInterface>
}
src/DomainModel/Reply/ReplyService.js
import {ReplyRepositoryInterface} from "./ReplyRepositoryInterface";
import {ReplyInterface} from "./ReplyInterface";
import {ReplyNotFoundError} from "./ReplyNotFoundError";
export class ReplyService {
constructor(
private replyRepository: ReplyRepositoryInterface
) {
}
public async getReply(intent: string): Promise<string> {
let reply: ReplyInterface
try {
reply = await this.replyRepository.findOneByIntent(intent);
} catch (e) {
if (!(e instanceof ReplyNotFoundError)) {
throw e
}
reply = {text: `Sorry, I cannot help you with '${intent}' in this moment...`};
}
return reply.text;
}
}
Here you can see that ReplyService has a dependency on ReplyRepositoryInterface but, how can we mock this interface to test our service in isolation as a real unit test?
To mock a TypeScript interface in jest, you only need an object that has the same functions as the interface. In our case, we need to mock a function that returns a promise. We can do that with jest.fn():
const replyRepositoryMock = {
findOneByIntent: jest.fn().mockReturnValue(Promise.resolve({text: replyText}))
};
And this is how one of the tests would look like:
src/DomainModel/Reply/ReplyService.test.js
import {ReplyService} from "./ReplyService";
test('getReply returns the expected reply text', () => {
const intent = 'baz'
const replyText = 'ok'
const replyRepositoryMock = {
findOneByIntent: jest.fn().mockReturnValue(Promise.resolve({text: replyText}))
};
const replyService = new ReplyService(replyRepositoryMock);
return replyService
.getReply(intent)
.then(reply => {
expect(replyRepositoryMock.findOneByIntent).toBeCalledWith(intent);
expect(reply).toBe(replyText)
})
});
Jest is very flexible and it also allows you to mock entire packages, like axios:
src/Infrastructure/UltimateAi/IntentSearchService.test.js
import {IntentSearchService} from "./IntentSearchService";
import axios from "axios";
jest.mock('axios');
test('example axios mock', () => {
const resp = {
data: {
intents: [
{name: "Foo", ratio: 0.7},
{name: "Bar", ratio: 0.2},
{name: "Baz", ratio: 0.5},
{name: "Foo2", ratio: 0.71},
{name: "Bar2", ratio: 0.2},
]
}
};
const axiosMock = axios as jest.Mocked<typeof axios>
axiosMock.create.mockReturnValue(axiosMock)
axiosMock.post.mockResolvedValue(resp)
const client = new IntentSearchService(axiosMock)
});
As you can see you can mock pretty much anything with Jest, it's pretty simple and you don't need any other libraries to accomplish the same.