Guide on how to utilize jest for mocking MongoDB manager in typescript

Is there a way to return a mongodb.connection.db() type value and mock its collection for testing purposes? I have implemented a mongoClient connection and use its db() function. If everything is set up correctly, I should be able to access the collections but I am finding it difficult to write tests due to the inability to mock it.

dbManager.ts

import { MongoClient } from 'mongodb';

class DBManager {
  private connection?: MongoClient;
  private mongoUrl = null;

  constructor(url: string) {
    this.mongoUrl = url;
  }

  get db() {
    return this.connection!.db();
  }

  async start() {
    if (!this.connection) {
      this.connection = await MongoClient.connect(this.mongoUrl);
    }
  }
}

export default DBManager;

index.ts

 const dbManager = new DBManager(url);
  await dbManager.start();
  const db = dbManager.db;

  if (db) {
    const collection = db.collection(collectionName);
  }

index.spec.ts

  const dbManager = new DBManager( 'mongoUrl');
      jest.spyOn(dbManager, 'start').mockResolvedValue();
      jest.spyOn(dbManager, 'db', 'get').mockImplementation();

Answer №1

The reason behind the failure of the mock is due to creating a new instance of DBManager in the test case, and jest.spyOn() only spies on the methods of this specific instance. The tested code contains another instance of DBManager, which still calls its original unspied method.

To resolve this issue, you should spy on the DBManager.prototype.start() and DBManager.prototype.get() methods.

Additionally, remember to restore mocks back to their original values in the afterEach hook to ensure that the mocked object from the test case does not impact other test cases.

For example:

DBManager.ts:

import { MongoClient } from 'mongodb';

class DBManager {
  private connection?: MongoClient;
  private mongoUrl: string = '';

  constructor(url: string) {
    this.mongoUrl = url;
  }

  get db() {
    return this.connection!.db();
  }

  async start() {
    if (!this.connection) {
      this.connection = await MongoClient.connect(this.mongoUrl);
    }
  }
}

export default DBManager;

index.ts:

import DBManager from './dbManager';

export async function main() {
  const url = 'mongodb://localhost:27017';
  const collectionName = 'user';
  const dbManager = new DBManager(url);
  await dbManager.start();
  const db = dbManager.db;

  if (db) {
    const collection = db.collection(collectionName);
  }
}

index.test.ts:

import { main } from './';
import DBManager from './dbManager';
import { Db } from 'mongodb';

describe('69011729', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  test('should pass', async () => {
    const mDB = ({
      collection: jest.fn(),
    } as unknown) as Db;
    jest.spyOn(DBManager.prototype, 'start').mockResolvedValue();
    jest.spyOn(DBManager.prototype, 'db', 'get').mockReturnValueOnce(mDB);
    await main();
    expect(DBManager.prototype.start).toBeCalledTimes(1);
    expect(mDB.collection).toBeCalledWith('user');
  });

  test('should restore original methods', () => {
    expect(jest.isMockFunction(DBManager.prototype.start)).toBeFalsy();
  });
});

Test result:

 PASS  examples/69011729/index.test.ts (10.734 s)
  69011729
    ✓ should pass (4 ms)
    ✓ should restore original methods

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        11.516 s
Ran all test suites related to changed files.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Typescript constructor that accepts an object as an argument instead of traditional parameters

My constructor is becoming lengthy and not structured the way I would prefer. I am looking to pass an object to my constructor so that I can access fields by their names. Here is how the class looks currently. export class Group { id: string; constru ...

Utilize an enum to serve as a blueprint for generating a fresh object?

I've defined an enum as shown below: export enum TableViewTypes { user = 'users', pitching = 'pitching', milestones = 'milestones', mediaList = 'mediaList', contacts = 'contacts' } ...

Is it possible to modify a single value in a React useState holding an object while assigning a new value to the others?

In my current state, I have the following setup: const [clickColumn, setClickColumn] = useState({ name: 0, tasks: 0, partner: 0, riskFactor: 0, legalForm: 0, foundationYear: 0 }) Consider this scenario where I only want to update ...

An error occurs with webpack during postinstall when trying to load TypeScript

I have created a custom package that includes a postinstall webpack script as specified in my package.json file: "scripts": { ... "postinstall": "webpack" } The webpack configuration looks like this: const path = require('path'); ...

Obtain merged types by accessing a particular property within a deeply nested object

My query is reminiscent of a post on Stack Overflow titled Get all value types of a double-nested object in TypeScript However, my specific requirement involves extracting union types from the values of a designated property. const tabsEnum = { IDCardRe ...

Automatically convert TypeScript packages from another workspace in Turborepo with transpilation

I have set up a Turborepo-based monorepo with my primary TypeScript application named @myscope/tsapp. This application utilizes another TypeScript package within the same repository called @myscope/tspackage. For reference, you can view the example reposit ...

Issue encountered with express-jwt and express-graphql: TypeScript error TS2339 - The 'user' property is not found on the 'Request' type

Implementing express-jwt and graphql together in typescript has been a challenge for me. import * as express from 'express' import * as expressGraphql from 'express-graphql' import * as expressJwt from 'express-jwt' import s ...

Implementing an extended interface as an argument in a function

Here is the code snippet for analysis: interface IUserData { FirstName: string, LastName: string, Email: string, Password: string } interface IState extends IUserData { isSuccess: boolean } const state: IState = { FirstName: &apo ...

React Router throwing an error about an Invalid Hook Call when attempting to use useState in index.tsx instead of App.js

I'm currently learning React Router by following a video tutorial, but I've run into an issue. In my Stackblitz project, there is no App.js file, so I've placed everything inside index.tsx. However, now I need to use the line ----> const ...

Angular does not permit the use of the property proxyConfig

Click here to view the image I encountered an issue when attempting to include a proxy config file in angular.json, as it was stating that the property is not allowed. ...

Serialising and deserialising TypeScript types in local storage

I'm currently working on a Typescript application where I store objects using local storage for development purposes. However, I've run into some trouble with deserialization. Specifically, I have an object called meeting of type MeetingModel: ...

Step-by-step guide on releasing declaration files (.d.ts) for vscode plugins

I developed a vscode extension that provides an API for other extensions to utilize (by returning a value in the activate() function). I am interested in releasing a scoped npm package containing a declaration file (.d.ts) to help extension developers eas ...

Instructions on how to post an array by its ID when the value changes in the form, correspond with the ID

Whenever I change the value in the radio button within a form popup, I want to trigger this action. Below is the corresponding HTML code: <ng-container cdkColumnDef="injected"> <mat-header-cell *cdkHeaderCellD ...

Unexpected token @ while using Angular2 with jspm and gulp for typescript compilation

Recently, I've delved into learning about Angular 2 and its accompanying technologies. In an attempt to create minified and "compiled" versions of my .ts files, I started using gulp-jspm-build. However, I encountered an error that has left me stumped. ...

Creating non-changing identifiers with ever-changing values in Angular by leveraging TypeScript?

I need to transform all the labels in my application into label constants. Some parts of the HTML contain dynamic content, such as This label has '1' dynamic values, where the '1' can vary based on the component or a different applicat ...

Testing the $resource PUT request in AngularJS

Within my AngularJS 1.2.10 application, everything runs smoothly except for the unit tests. Whenever I attempt to update a record (HTTP PUT) using $httpBackend, the callbacks fail to trigger, leading to test failures. The specific controller method under ...

Implementing type inference for response.locals in Express with TypeScript

I need to define types for my response.locals in order to add data to the request-response cycle. This is what I attempted: // ./types/express/index.d.ts declare global { declare namespace Express { interface Response { locals: { ...

The phpUnit code coverage analysis for interfaces is not yielding positive results

Currently, I am conducting a whitebox test where the parent class and all interfaces are thoroughly tested. To ensure coverage, I have added the following doc comment: /** * @covers \Pat\Environment\PHP\Autoloader\Psr0<extende ...

Creating a service in AngularJS 1.8 with ES6 modules that acts as a bridge to a class based API interface

As I continue to enhance a codebase that originally consisted of a mix of different versions of AngularJs and some unstructured code utilizing various versions of a software API, I encounter an interesting quirk. It appears that this API is accessible thro ...

What is the best way to wait for the state to be set before mapping the array

I have some data stored in an array (shown in the screenshot below) that I am trying to map, but I am facing issues accessing it as it is loaded asynchronously. How can I await the data? Is there a way to achieve this within the render function? render() ...