Home > front end >  Jest how to mock a typescript class but alternate some method behavior in only 1 test and not modify
Jest how to mock a typescript class but alternate some method behavior in only 1 test and not modify

Time:12-29

I have a typescript class userInfo.ts:

export class UserInfo {
  public getName() {
    return "I am real name";
  }
}

I have a mocked class userInfo.ts in mocks folder:

export class UserInfo {
  public getName() {
    return "I am fake name";
  }
}

I have a client:

import { UserInfo } from "./userInfo";
export class Client {

  public functionToTest() {
    let validation = new UserInfo();
    return validation.getName();
  }

}

And finally I want to have TWO tests for this, in the first one I want to overwrite the getName mock only for this test, and in the second one I want to have the mocked class behavior so:

import { Client } from "./client";
import { UserInfo } from "./userInfo";

jest.mock("./userInfo");
const userInfoMocked = UserInfo as jest.MockedClass<typeof UserInfo>; // I tried with this but with no success

describe("Client", () => {

  it("should get Name", () => {
    let client = new Client();
    // UserInfo.prototype.getName = jest.fn().mockImplementationOnce(() => {
    //   return "Something weird happened";
    // });
    userInfoMocked.prototype.getName = jest.fn().mockImplementationOnce(() => {
      return "something weird happend";
    });

    // this is not working either
    // Property 'getName' does not exist on type 'MockedClass<typeof UserInfo>'.
    // userInfoMocked.getName = jest.fn().mockImplementationOnce(() => {
    //   return "something weird happend";
    // });

    let text = client.functionToTest();
    expect(text).toBe('something weird happend'); 
    let text2 = client.functionToTest();
    expect(text2).toBe('I am fake name'); // I get undefined (I overwrote prototype!)
  });

  it('should get fake name now', () => {
    let client = new Client();
    let text3 = client.functionToTest();
    expect(text3).toBe('I am fake name'); // I get undefined

  });
});

I am suprised that such a common (I think) functionality is not achievable? How to succeed in this? Is this even possible?

CodePudding user response:

You can assign a default implementation to the mock:

import userInfo from "./userInfo"
jest.mock("./userInfo", () => ({
  getName: jest.fn(() => 'John Doe'),
}));

And each time you want to overwrite the implementation:

userInfo.getName.mockImplementation(() => jest.fn().mockReturnValue('another value'));

CodePudding user response:

If you want to use different mocks in different tests, don't use the mocks folder. Instead create the mocks you need for each test. This describes the different types of mocking you can do. Based on your description I would use mockImplementation. eg in one test you could do

UserInfo.mockImplementation(() => {
  return {
    getName: () => {
      return 'I am a real name'
    }
  }
}

And in another test:

UserInfo.mockImplementation(() => {
  return {
    getName: () => {
      return 'I am a fake name'
    }
  }
}

All the methods boil down to the same thing so it's a question of picking the one that fits in best with the structure of your code and is easy to maintain.

CodePudding user response:

Here is my solution to this: It occurred that when not using manual mock (thus having UserInfo.ts under mocks) it does not work at all, but when getting rid of that manual mock and left automock, I could do:

import { mocked } from "ts-jest";
import { Client } from "./secretsManagerWrapper";
import { UserInfo } from "./userInfo";

jest.mock("./userInfo");
const userInfoMocked = mocked(UserInfo, false);

describe("Client", () => {
  it.only("should get Name", () => {
    let client = new Client();

    userInfoMocked.mockImplementationOnce(function () {
      return {
        getName: () => {
          return "John Doe Second";
        },
      };
    });

and it worked !

I discovered also that when I do want to use manual mock (under mocks folder) I can use a Spy:

describe("Client", () => {
  it("should get Name", () => {
    let client = new Client();

    jest.spyOn(userInfoMocked.prototype, "getName").mockImplementationOnce(() => {
      return "John Doe Second";
    });

    let text = client.functionToTest();
    expect(text).toBe("John Doe Second");
    let text2 = client.functionToTest();
    expect(text2).toBe("I am fake name"); // I get what I want now
  });

  it("should get fake name now", () => {
    let client = new Client();
    let text3 = client.functionToTest();
    expect(text3).toBe("I am fake name"); // I get what I want now !
  });
});

And I can accomplish what I wanted :)

  • Related