Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jest.mock factory doesn't work inside a test #2582

Closed
tleunen opened this issue Jan 12, 2017 · 54 comments
Closed

jest.mock factory doesn't work inside a test #2582

tleunen opened this issue Jan 12, 2017 · 54 comments

Comments

@tleunen
Copy link

tleunen commented Jan 12, 2017

Do you want to request a feature or report a bug?
Bug

What is the current behavior?

It seems the way to create a mock with a factory doesn't work inside test or it. It works only when the mock is defined at the root level of the file.

Here's my example of a mock:

jest.mock('services/feature', () => ({
    isEnabled: () => true
}));

What is the expected behavior?

Mocking a file inside a test should work.

Please provide your exact Jest configuration and mention your Jest, node, yarn/npm version and operating system.

Jest 18.0.0, Node 7.4, macOS

@thymikee
Copy link
Collaborator

jest.mock calls are automatically hoisted to the top of the file with babel-jest transform. You can omit this behaviour with jest.doMock. Have you tried that?

@tleunen
Copy link
Author

tleunen commented Jan 12, 2017

Same thing with doMock.

In the docs, I can read

Note: When using babel-jest, calls to mock will automatically be hoisted to the top of the code block. Use doMock if you want to explicitly avoid this behavior.

But... A test is a code block, right? So in my case, I don't expect to see any differences.

Here's a full test

it('renders with the enabled feature', () => {
  jest.mock('services/feature', () => ({
      isEnabled: () => true
  }));

  const component = renderer.create(
      <MyComponent />
  );

  const tree = component.toJSON();
  expect(tree).toMatchSnapshot();
});

@thymikee
Copy link
Collaborator

Could you provide a repro of this in a GH repository?

@tleunen
Copy link
Author

tleunen commented Jan 13, 2017

Sure. Here it is: https://github.com/tleunen/jest-issue-2582

Both tests render "Disabled" even though one of then has a mock to renders "Enabled" instead.
See these:
https://github.com/tleunen/jest-issue-2582/blob/master/src/MyComponent.js
https://github.com/tleunen/jest-issue-2582/blob/master/src/__tests__/MyComponent.spec.js

Thanks.

@tleunen
Copy link
Author

tleunen commented Jan 30, 2017

Any advice @thymikee @cpojer for this issue?
I have several tests in the same file and I'd like to have different mock responses for each of them.

@publicJorn
Copy link

publicJorn commented Mar 2, 2017

Glad I found this issue, I was breaking my head whyjest.mock() didn't work in my describe scope. Moved it to the top (below my imports in the test file) and it works.

For me it also applies to jest.mock() without a factory, using a __mocks__ folder containing the mocked file.

--edit

It's obviously necessary to hoist the jest.mock() statement to before the import statements. However @tleunen, this probably means it's not possible to mock the same file more then once, with different responses. Maybe you are best served by making the factory function more dynamic, so it can serve you different results in each test case.

In my mind it would make it more explicit if jest.mock() is always put outside the describe and it blocks. But this should then be clearly stated in the docs

@thymikee
Copy link
Collaborator

thymikee commented Mar 6, 2017

So jest.mock is being hoisted to the function scope, that's why it won't work with requires (and definitely not imports which are hoisted to module scope) if you call it inside a function other than describe (which is treated specially by Jasmine).
Your jest.mock call will be hoisted to the top of that very function (not the module), that's why it won't work the way you expect.

Generally we advise to setup different mocks in beforeEach and afterEach if you want them different across test cases.

@cpojer could elaborate on this in detail, and if we want to hoist the calls to the upper scopes.

@cpojer
Copy link
Member

cpojer commented Mar 6, 2017

This is because you are requiring your modules when the module initializes (using import). jest.mock gets called way later. The way to solve this is:

beforeEach(() => { // or the specific test
  jest.mock('MyModule', () => );
  const MyModule = require('MyModule');
  
});

etc.

@cpojer cpojer closed this as completed Mar 6, 2017
@Psykar
Copy link

Psykar commented Mar 8, 2017

If you were to do this in beforeEach, I'm unclear how you'd differentiate tests (so how would you be giving a different mock for each test?)

Putting it inside the tests themselves works of course.

@MartinCerny-awin
Copy link

Can you then pass mocked module inside the tested component?

@gabrielhpugliese
Copy link

gabrielhpugliese commented Aug 10, 2017

What if I am not importing/requiring the file I want to mock (e.g. a dependency of other file I am importing) but I want it scoped to a describe/it block? Or even if I want to mock differently for beforeEach/beforeAll test? Are those cases possible?

// A.js depends on B.js
import A from './A';

describe('myTest', () => {

    describe('myFirstScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                myFirstMethod: jest.fn(),
            }));
        });

        // tests here
    });

    describe('mySecondScope', () => {
        beforeAll(() => {
            jest.mock('./B', () => ({
                mySecondMethod: jest.fn(),
            }));
        });

        // tests here
    });
});

@tleunen
Copy link
Author

tleunen commented Aug 10, 2017

In that case, you need to require A after mocking B. (Not using import, but require).

@GoldAnna
Copy link

GoldAnna commented Oct 2, 2017

requiring A after mocking B

didn't work for me, was still seeing the original mock of B pass through to the result

@alayor
Copy link

alayor commented Nov 23, 2017

@GoldAnna have you found a workaround for this issue?

@GoldAnna
Copy link

@alayor I have not

@gcox
Copy link

gcox commented Nov 24, 2017

As many times as I've run into this problem, I'm now convinced I'm either not testing properly, not using jest as the authors intended, or some combination of both. Further proof is that almost none of the mock related examples in jest's documentation seem like real world examples to me...they probably are and my approach is probably incorrect.

That said, the following works for me, and all of the following tests pass. Note that to get back to the original version of ModuleB, I have to call both jest.resetModules() and jest.unmock('./moduleB')...the order of those doesn't matter.

// Module A
const ModuleB = require('./moduleB');
const ModuleA = function() {
  this.title = new ModuleB().title;
  return this;
};
module.exports = ModuleA;

// Module B
const ModuleB = function() {
  this.title = 'Module B - Original'
  return this;
};
module.exports = ModuleB;

// Tests
describe('Jest a few tests', () => {
  it('should do something', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 1'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 1');
  });

  it('should do something else', () => {
    jest.resetModules();
    jest.mock('./moduleB', () => function() {
      this.title = 'Module B - Mock 2'
      return this;
    });
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Mock 2');
  });

  it('should do something original', () => {
    jest.resetModules();
    jest.unmock('./moduleB');
    const ModuleA = require('./moduleA');
    const moduleA = new ModuleA();
    expect(moduleA.title).toEqual('Module B - Original');
  });
});

@bhd1bhd1
Copy link

Hey,

Nothing here works for me.
Can someone please explain why it is not possible to use jest.mock inside it?

Thank you

@antgonzales
Copy link

I think this is still an issue that should maybe be reopened as a feature request. I want to write tests in isolation. Dumping things in a mocks folder or rewiring with a beforeEach usually ends up with a junk drawer of mocks/weird data instantiation that gets lugged around into every test. I want to write a small mock and make a small test pass.

@SimenB
Copy link
Member

SimenB commented Apr 4, 2018

We have no real way of isolating individual tests (considering test.concurrent). If we adopted an API similar to tap or ava it'd be possible, but I don't think it's possible with the constraints imposed by the current global API. Super happy to be proven wrong!

@zhenyulin
Copy link

Had a struggle with multiple methods here to refactor a test previously written in mocha and proxyquire, end up with separating the test into different files for different mock.

@jkomusin
Copy link

jkomusin commented Apr 4, 2018

@gcox Have you tried mocking (and requiring moduleA after) at the top-level of the module and then changing the mock's implementation for each test that needs different behavior? You can use mockImplementation to do so. This is how we've solved this in our test suites.

@antgonzales I think that's difficult to do if module imports are hoisted to the top of a module. Modifying a previously-imported module's references seems non-trivial to impossible in node.

@gcox
Copy link

gcox commented Apr 4, 2018

@jkomusin Definitely. That's just not what the OP was asking for, IMO. I believe they were asking, specifically, "How do I force require('whatever') to return a different mock across multiple adjacent tests rather than the same object?".

@SimenB
Copy link
Member

SimenB commented Apr 4, 2018

To change return value of a mock between tests, you can do something like this:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

@rafaeleyng
Copy link

@gcox answer did work for me, but @SimenB also did work, and is so much simpler!

@christopher-francisco
Copy link

@rafaeleyng but @SimenB doesn't work if what you export from the module is not a function...

@jhfdx8
Copy link

jhfdx8 commented Oct 22, 2019

Jasmine spyOn() has no such problem inside tests. Jest supposedly uses jasmine by default? Why doesn't it work inside test?

Jasmine Example:

spyOn(require('moduleB'), 'functionA').and.callFake(() => true);

@c-goettert
Copy link

c-goettert commented Feb 7, 2020

What I don't quite understand when it comes to mocking modules is that it is always described as if you want to use the module primarily in your test-functions. E.g. if you look on the example code for the doMock function in the docs:

test('moduleName 2', () => {
  jest.doMock('../moduleName', () => {
    return {
      __esModule: true,
      default: 'default2',
      foo: 'foo2',
    };
  });
  return import('../moduleName').then(moduleName => {
    expect(moduleName.default).toEqual('default2');
    expect(moduleName.foo).toEqual('foo2');
  });
});

In this example, you have access to the newly mocked version of "moduleName" inside the test function only. For me it is way more important that my "non-test"-code would have access to the mocked version of the module as well. E.g. a Class that I am using inside my test, that has imported "moduleName" would still be using the original version, not the mocked one.

Seems to be related: #3236

@jonathanconway
Copy link

Tip - if you have a module that exports a primitive value, e.g.:

export const isUserAdmin = getSetting('admin');

And you want to use a mock value instead, in the test, then a simple require and assignment seems to do the trick:

const auth = require('../auth');

describe('deleteUser', () => {
  it('can delete if admin', () => {
    auth.isUserAdmin = true;

    // call method that depends on isUserAdmin value
    // and assert on the result
  });
});

@AntonioRedondo
Copy link

AntonioRedondo commented Jul 7, 2020

If you want to mock an object that is used indirectly by the code tested the jest.doMock() function won't work:

import myModuleToTest from './myModuleTotest'

describe('Given my module', () => {
  it('property1 will work as expect', () => {
    // Testing parts of the module that don't need to be mocked
  })

  it('property2 will work as expected', () => {
    jest.doMock('./myOtherModule', () => {
      return {
        __esModule: true,
        default: 'default2',
        foo: 'foo2',
      };
    });

    import('./myOtherModule').then(myOtherModule => {
      // I'm not interested on the mocked module myOtherModule but on the module that makes use of it
      myModuleToTest.doSomethingToSomeProperty(); // At this point myOtherModule's original module and not its mocked version will be used by myModuleToTest
      expect(myModuleToTest.someProperty).toBe('thisWillFail'); // The test won't pass because the mocked version wasn't used
    });
  });
});

As of Jest 26 there is no way to mock more than once a module exporting an Object that is used indirectly (mocking something else than a Function since there is no mockFn.mockImplementation(fn) for Objects).

The only solution then is having more than one test file to test the same module.

@gabrielhpugliese
Copy link

gabrielhpugliese commented Jul 8, 2020 via email

@vhd-luke
Copy link

vhd-luke commented Feb 17, 2021

None of the solutions mentioned seemed to work for me, so I added this above my describe

jest.mock('date-fns-tz', () => ({
    getTimezoneOffset: jest.fn().mockImplementation((timezone, date) => {
        return timezone
    })
}));

Then I updated the mocked data that was being passed into the getTimezoneOffset function in each test and that worked.

@pwob
Copy link

pwob commented Oct 26, 2021

Just encountered this really tedious bit of error (react), but I think I figured it out. The solution was staring at me all this time. this bit of info in the documentation is the key:

Warning: Importing a module in a setup file (as specified by setupFilesAfterEnv) will prevent mocking for the module in question, as well as all the modules that it imports.

This means that if you happen to have imported a module that also uses/imports that module (that you are about to mock) in the setup file, you will not be able to mock that module! In my case, I was refactoring my code so that everything that relates to testing is in one export (index) file (including the methods I was about to mock! It's a Graphql + MSW setup).

@shaikhrahil
Copy link

After going through a number of solutions online, a lot of documentation , and none of them working out, I found something that works for me. Please feel free to update / improve on it

So instead of mocking at the top, I started creating a mockRender variable and run it beforeEach. Code example:

describe('dashboard.tsx', () => {
  let mockRender
  beforeEach(() => {
    mockRender = () =>
      render(
        <CustomAppWrapper
          init={{
            firstName: 'Rahil',
            lastName: 'Shaikh',
            id: 123,
          }}
        >
          <Dashboard />
        </CustomAppWrapper>,
      )
  })

  it('shows loader', () => {
    useStats.mockImplementation(() => ({ connecting: true, listen: jest.fn() }))
    mockRender()
    expect(screen.getByText('Loading ...')).toBeDefined()
  })

  it('shows error message', () => {
    const error = 'Unable to load'
    useStats.mockImplementation(() => ({ error, listen: jest.fn() }))
    mockRender()
    expect(screen.getByText(error)).toBeDefined()
  })
})

This is a sample of what I have implemented. Hope it helps out any one stuck with this ✌

@tleunen @SimenB @cpojer @thymikee

@atifsaddique211f
Copy link

This is what worked for me to mock a custom hook being used inside code for specific test suite.

import * as useCustomHook from './useCustom'

describe('My test suite', () => {
  const mockSaveMethod = jest.fn();
  const mockDeleteMethod = jest.fn();

  const clickOnElementByTestId = (testId: string) => {
    fireEvent.click(screen.getByTestId(testId));
  };

  beforeEach(() => {
    jest.spyOn(useCustomHook, 'useCustom').mockImplementation(() => {
      return {
        saveMethod: mockSaveMethod,
        deleteMethod: mockDeleteMethod
      };
    });
  });

  test('should call save on clicking save button', async () => {
    render('<MyComponent />');
    clickOnElementByTestId('testIds.saveButton');
   await waitFor(() => {
      expect(mockSaveMethod).toHaveBeenCalledTimes(1);
    });
  });

  test('should call publish on clicking publish button', async () => {
    render('<MyComponent />');
    clickOnElementByTestId('testIds.publishButton');
     await waitFor(() => {
        expect(mockDeleteMethod).toHaveBeenCalledTimes(1);
      });
  });
});

and useCustom hook would be something like this

export function useCustom(){
   const saveMethod=()=>{};
   const deleteMethod=()=>{};

  return {
    saveMethod,
    deleteMethod
  }
}

and the component under testing MyComponent will be something like this

export function MyComponent(){
   const { saveMethod, deleteMethod } = useCustom();
   
   ...
   ...
}

@apperside
Copy link

@janl 's answer brought me toward the right direction.

The solution which works for me is to call jest.mock("my-module-name") at the beginning of the tests ( I do it in a test initialization file which I import in all my tests), and then, by requiring the module in your tests, you get the mocked version of it because it has been mocked at the beginning. From there you have full access to all properties and functions exported by the module and you can mock everything you need.

test-init.js

jest.mock("my-module-name")
//... other stuff

my-component.test.js

// here you will get the mocked version
const myLibOrModule = require('my-lib-or-module');


describe("Me tests", () => {

  
  test('test 1', async () => {
	// depending on what your lib or module exports (it can export a function or an object)
	// you can mock anything it exports

	// if the module exports a function
        myLibOrModule.mockImplementation((...args) => {
		// you custom return value
        })
	// OR 
	myLibOrModule.mockReturnValue({
		// your obj
	})

	//if your module exports an object with function fields
	myLibOrModule.myFunction.mockImplementation((...args) => {
		// you custom return value
    })

   //...your tests

  });

  test('test 2', async () => {
       // in the same way as in test 1, you can write mocks for this specific test

	// if the module exports a function
        myLibOrModule.mockImplementation((...args) => {
		// you custom return value
        })
	// OR 
	myLibOrModule.mockReturnValue({
		// your obj
	})

	//if your module exports an object with function fields
	myLibOrModule.myFunction.mockImplementation((...args) => {
		// you custom return value
    })

	//...your tests
  });
})

@itsjwala
Copy link

itsjwala commented May 8, 2022

another solution to this issue is creating __mocks__/module_to_mock

  • A.js depends on module_to_mock

  • A.spec.js

require('A')
jest.mock('module_to_mock')

// .....
// tests here

@m1chae1bx
Copy link

Just encountered this really tedious bit of error (react), but I think I figured it out. The solution was staring at me all this time. this bit of info in the documentation is the key:

Warning: Importing a module in a setup file (as specified by setupFilesAfterEnv) will prevent mocking for the module in question, as well as all the modules that it imports.

This means that if you happen to have imported a module that also uses/imports that module (that you are about to mock) in the setup file, you will not be able to mock that module! In my case, I was refactoring my code so that everything that relates to testing is in one export (index) file (including the methods I was about to mock! It's a Graphql + MSW setup).

This worked for me. I had stopped importing the module (that I'm testing) at the top level, and then just do an import in the test level instead.

import { testUserInit } from "../test-data/test-user-init";

describe("create", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  describe("happy path", () => {
    let result: string;

    beforeAll(async () => {
      jest.doMock("/opt/nodejs/dynamo.config", () => ({
        dynamoClient: {
          put: jest.fn().mockReturnValueOnce({
            promise: jest.fn().mockResolvedValueOnce(null),
          }),
        },
        TABLE_NAME: "test-table-name",
      }));
      const userDao = await import("../user.dao");
      result = await userDao.create(testUserInit);
    });

    it("should return the user ID", () => {
      expect(result).toBe(testUserInit.id);
    });
  });
  
  // ...
});

../user.dao is the module I'm testing while /opt/nodejs/dynamo.config is the module I'm mocking. I cannot use jest.mock here because I have a primitive data type variable TABLE_NAME that I need to have different values in some of my tests, hence the use of jest.doMock.

@rickwillcox
Copy link

rickwillcox commented Aug 17, 2022

@apperside solution is the one that finally worked me me.
#2582 (comment)

Set up
In package.json

"jest": {
    "preset": "jest-expo",
    "transformIgnorePatterns": [
      "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
    ],
    "setupFiles": [
      "./jestSetup.ts"
    ]
  },

Then in jestSetup.ts

jest.mock('moduleToBeMocked');

Then in test file app.test.ts

const mockedModule = require('moduleToBeMocked');

test('Some test', async () => {
   mockedModule.mockedModuleFunction.mockImplementation(
     () => "hello world",
   );
   const result = mockedModuleFunction();
   expect(result).toBe("hello world");
 });

Should note that this also works if mockedModuleFunction is being called from another function. So say mockedModuleFunction is called by otherFunction. For example if otherFunction looked like this:

 function otherFunction() {
    return mockedModuleFunction()
}

Then this will also work.

 const mockedModule = require('moduleToBeMocked');
 
test('Some test', async () => {
    mockedModule.mockedModuleFunction.mockImplementation(
      () => "hello world",
    );
    const result = otherFunction();
    expect(result).toBe("hello world");
  });

@ParallelTask
Copy link

By using jest.mock and jest.spyOn, we can mock inside every test with different implementation

// Header.js
export function Header() {
  return (<div data-testid="header" className="App-header">I am header</div>);
}

// App.js
import { Header } from "./components/Header";
export function App() {
    return <Header />;
}

**Scenario 1- mocking inside every test**

// App.test.js
import { render, screen } from "@testing-library/react";
import { App } from "./App";

let mockHdrObj = { Header: () => <></>};
jest.mock("./components/Header", () => mockHdrObj);

describe("AppComponent", () => {

  it("App component with current mocked hdr", async () => {
    jest.spyOn(mockHdrObj, "Header").mockReturnValue(<div data-testid="header">Mocked Header</div>);
    render(<App />);
    const hdr = await screen.getByTestId("header");
    expect(hdr.innerHTML).toEqual("Mocked Header");
  });

  it("App component with original mocked hdr", async () => {
    jest.spyOn(mockHdrObj, "Header").mockRestore();
    render(<App />);
    const hdr = await screen.queryByTestId("header");
    expect(hdr).toBeNull();
  });
});

**Scenario 2- If you want to hold the original implementation for some test cases**

// PureJestMock.js
import * as hdrModule from "./components/Header";

var mockHdrObj = { Header: hdrModule.Header }; // if you still want to hold to the original implementation
jest.doMock("./components/Header", () => mockHdrObj); // just doMock which does not do hoisting
export default mockHdrObj;

// App.test.js
import { render, screen, waitFor } from "@testing-library/react";
import mockHdrObj from "./PureJestMock";
import { App } from "./App"; // You should import the testable component only after importing the above-mocked objects

describe("App Component", () => {

  it("App component with empty header", async () => {
    jest.spyOn(mockHdrObj, "Header").mockReturnValue(<></>);
    render(<App />);
    const hdr = await screen.queryByTestId("header");
    expect(hdr).toBeNull();
  });

  it("App component with mocked hdr", async () => {
    jest.spyOn(mockHdrObj, "Header").mockReturnValue(<div data-testid="header">Mocked Header</div>);
    render(<App />);
    const hdr = await screen.getByTestId("header");
    expect(hdr.innerHTML).toEqual("Mocked Header");
  });

  it("App jest component with actual hdr", async () => {
    jest.spyOn(mockHdrObj, "Header").mockRestore();
    render(<App />);
    const hdr = await screen.getByTestId("header");
    expect(hdr.innerHTML).toEqual("I am header");
  });
});

@Raffaello
Copy link

Raffaello commented Oct 6, 2022

The only way it looks like working when it needs to mock a module used as a transitive dependency, is to mock the transitive module as usual.

Then to override its possible results, just create a spy on the mocked module.
Kind of a hack but it works.

@baspinarenes
Copy link

baspinarenes commented Oct 23, 2022

I will share my example for those in the same situation. I tried with the following codes:

// utils.ts
import axios from "axios";

export async function get(apiUrl: string): Promise<any> {
  try {
    const response = await axios.get(apiUrl);

    return response.data;
  } catch (error) {
    return null;
  }
}
// utils.test.ts
import { get } from "./utils";

describe("utils tests", () => {
  describe("get() tests", () => {
    test("should return product when request is success", async () => {
      jest.mock("axios");
      const axios = require("axios");
      const mockedAxios = jest.mocked(axios);

      const apiUrl = "https://dummyjson.com/product/1";
      const mockProduct = {
        id: 1,
        title: "iPhone 9",
        description: "An apple mobile which is nothing like apple",
        price: 549,
        discountPercentage: 12.96,
        rating: 4.69,
        stock: 94,
        brand: "Apple",
        category: "smartphones",
      };

      mockedAxios.get.mockResolvedValue({
        data: mockProduct,
      });

      const result = await get(apiUrl);

      expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
      expect(result).toStrictEqual(mockProduct);
    });
  });
});

And I couldn't mock it. I realized later. I needed to mock axios before importing the file I'm using it. So:

// utils.test.ts
describe("utils tests", () => {
  describe("get() tests", () => {
    test("should return product when request is success", async () => {
      jest.mock("axios");
      const axios = require("axios");
      const mockedAxios = jest.mocked(axios);
      const { get } = require("./utils");

      const apiUrl = "https://dummyjson.com/product/1";
      const mockProduct = {
        id: 1,
        title: "iPhone 9",
        description: "An apple mobile which is nothing like apple",
        price: 549,
        discountPercentage: 12.96,
        rating: 4.69,
        stock: 94,
        brand: "Apple",
        category: "smartphones",
      };

      mockedAxios.get.mockResolvedValue({
        data: mockProduct,
      });

      const result = await get(apiUrl);

      expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
      expect(result).toStrictEqual(mockProduct);
    });
  });
});

I can move it to beforeEach:

// utils.test.ts
describe("utils tests", () => {
  describe("get() tests", () => {
    let mockedAxios: jest.Mocked<any>;

    beforeEach(() => {
      jest.mock("axios");
      const axios = require("axios");
      mockedAxios = jest.mocked(axios);
    });

    test("should return product when request is success", async () => {
      const { get } = require("./utils");

      const apiUrl = "https://dummyjson.com/product/1";
      const mockProduct = {
        id: 1,
        title: "iPhone 9",
        description: "An apple mobile which is nothing like apple",
        price: 549,
        discountPercentage: 12.96,
        rating: 4.69,
        stock: 94,
        brand: "Apple",
        category: "smartphones",
      };

      mockedAxios.get.mockResolvedValue({
        data: mockProduct,
      });

      const result = await get(apiUrl);

      expect(mockedAxios.get).toHaveBeenCalledWith(apiUrl);
      expect(result).toStrictEqual(mockProduct);
    });
  });
});

And I can override the global mock (simplified):

import { get } from "./utils";

jest.mock("axios", () => {
  return {
    get: jest
      .fn()
      .mockRejectedValueOnce(new Error("Error occured when fetching data!")),
  };
});

describe("utils tests", () => {
  beforeEach(() => {
    jest.resetModules();
  });

  describe("get() tests", () => {
    test("should return product when request is success", async () => {
      const apiUrl = "https://dummyjson.com/product/1";
      const mockProduct = {
        id: 1,
        title: "iPhone 9",
        description: "An apple mobile which is nothing like apple",
        price: 549,
        discountPercentage: 12.96,
        rating: 4.69,
        stock: 94,
        brand: "Apple",
        category: "smartphones",
      };

      jest.doMock("axios", () => ({
        get: jest.fn().mockResolvedValueOnce({
          data: mockProduct,
        }),
      }));
      const { get } = require("./utils");

      const result = await get(apiUrl);

      expect(result).toStrictEqual(mockProduct);
    });

    test("should return null when request is failed", async () => {
      const apiUrl = "https://dummyjson.com/product/1000";

      const result = await get(apiUrl);

      expect(result).toBeNull();
    });
  });
});

mock and doMock difference is only about hoisting. I hope it solves some people's problem.

@Diaa-Ghonim
Copy link

To change return value of a mock between tests, you can do something like this:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

worked like magic
very good @SimenB

@nickbling
Copy link

Another solution could be using an external variable that is changed inside each test.

let locale: string

jest.mock('@/hooks/useTranslation', () => ({
  useTranslation: () => {
    return {
      locale,
    }
  },
}))

describe('Test suite', () => {
  it('test english language', () => {
    locale = 'en'
    const component = render(<Component />)
    ...
  })

  it('test italian language', () => {
    locale = 'it'
    const component = render(<Component />)
    ...
  })
})

@diegods-ferreira
Copy link

@baspinarenes 's solution worked for me! Thanks!

@dnquang1996vn
Copy link

it works for me

import { methodToMock } from 'the-package-to-mock'

jest.mock('the-package-to-mock', () => ({
  methodToMock: jest.fn()
}));

it('test1', () => {
  methodToMock.mockImplementation(() => 'someValue')
})

it('test2', () => {
  methodToMock.mockImplementation(() => 'anotherValue')
})

@lesthoward
Copy link

To change return value of a mock between tests, you can do something like this:

jest.mock('whatever');

// Get the mock function
const whatever = require('whatever');

test('test 1', () => {
  whatever.mockImplementation(() => 'hello');
});

test('test 2', () => {
  whatever.mockImplementation(() => 'world');
});

worked like magic very good @SimenB

Worked to me as well, great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests