Back

Technologies:

javascriptjavascript
typescripttypescript
avatar
Tolerim
13 hours ago

What is the correct way to mock the simple-git library in Jest?

I am currently attempting to write Jest tests for my NestJS Application using the simple-git library. My goal is to simulate the return value of simpleGit() in order to analyze module behavior without executing Git commands. The relevant code for my module is as follows:
import simpleGit from "simple-git";

function myFunction() {
  const git = simpleGit();
  git.checkout("my-branch");
}
Additionally, I have a Jest test spec file that looks like this:
import myFunction from "./my-module";
import simpleGit, { SimpleGit } from "simple-git";

jest.mock("simple-git", () => {
  const mGit = {
    init: jest.fn(),
    checkout: jest.fn(),
    addConfig: jest.fn(),
    fetch: jest.fn(),
    pull: jest.fn(),
    checkIsRepo: jest.fn()
  };
  const mSimpleGit = jest.fn(() => mGit);
  return {
    default: mSimpleGit,
    SimpleGit: mSimpleGit
  };
});

describe("myFunction", () => {
  it("should call checkout with the correct branch", () => {
    myFunction();
    expect(simpleGit().checkout).toHaveBeenCalledWith("my-branch");
  });
});
Despite my efforts, the actual checkout function from simple-git is run instead of my mock. What am I doing wrong, and how can I properly mock the simple-git library in my Jest tests? The intended behavior is for the test to invoke the mocked checkout function.
Answers(1)
avatar
Tolerim
13 hours ago
Verified Answer
To properly mock the simple-git library in your Jest tests, you need to make sure that the mock implementation is returned by the same instance of simple-git that is used in your module's code. Currently, in your Jest test spec file, you are importing both simpleGit and SimpleGit from simple-git, but only mocking SimpleGit. This means that when your module's code runs const git = simpleGit();, it still uses the original implementation of simple-git, rather than the mocked one. To fix this, you should mock both simpleGit and SimpleGit, like this:
jest.mock('simple-git', () => {
  const mGit = {
    init: jest.fn(),
    checkout: jest.fn(),
    addConfig: jest.fn(),
    fetch: jest.fn(),
    pull: jest.fn(),
    checkIsRepo: jest.fn(),
  };
  const mSimpleGit = jest.fn(() => mGit);
  return Object.assign(mSimpleGit, mGit, {
    // This ensures that both named and default exports are mocked
    __esModule: true,
    default: mSimpleGit,
    SimpleGit: mSimpleGit,
  });
});
This also sets the __esModule property to true in the mock, which ensures that both named and default exports are mocked. Note that you can also simplify your module's code to accept a simple-git instance as a parameter, rather than creating it internally:
import simpleGit, { SimpleGit } from 'simple-git';

function myFunction(simpleGitInstance: SimpleGit) {
  const git = simpleGitInstance;
  git.checkout('my-branch');
}
And then in your test, you can pass the mocked instance to the function:
describe('myFunction', () => {
  it('should call checkout with the correct branch', () => {
    const gitMock = simpleGit();
    myFunction(gitMock);
    expect(gitMock.checkout).toHaveBeenCalledWith('my-branch');
  });
});
This way, you have more control over the simple-git instance used in your code, and can easily pass in a mock instance for testing.
;