Reputation: 5854
I am writing Unit test cases for my application. There is one function which is written in Utils section and Used in all files. I wanted to mock this Utils function whenever I need but I am unable to do so.
Here is my code setup:
Utils.js
> const getData = (name) => "Hello !!! " + name;
>
> const getContact = ()=> return Contacts.mobile;
>
> export {
> getData,
> getContact }
Login.js (Which uses Utils.js)
const welcomeMessage = (name) => {
return getData(name);
}
My Test file (Login.spec.js)
import { getData } from '../../src/utils';
jest.mock('getData', () => jest.fn())
describe('User actions', () => {
it('should get username', () => {
const value = 'Hello !!! Jest';
expect(welcomeMessage('Jest')).toEqual(value);
});
});
When I run my test case then I am getting this error:
Cannot find module 'getData' from 'Login.spec.js'
I tried to find the solution on official Jest Documentation and on SO as well but couldn't found anything. I am unable to fix this error and mock this function.
Upvotes: 45
Views: 116040
Reputation: 1194
jest.spyOn
fails in 2022I was trying to recreate sherwin waters solution using jest.spyOn
but it was not working. Don't know why.
UPDATE: Now I know why. I was using Create-React-App and with new version they changed default flag
resetMocks
is now set to true by default source. That's why we need to declare them inbeforeEach
as they are cleaned after each test.
// ./demo/welcomeMessage.js
import { getData } from "./utils"
export const WelcomeMessage = ({name}) => {
return getData(name)
}
// ./demo/utils.js
const getData = (name) => `Hello ${name}!`;
export { getData }
// ./App.js
import { WelcomeMessage } from "./demo/welcomeMessage";
function App() {
return (
<div className="App">
<h1>My app</h1>
<p>
<WelcomeMessage name={'John'} />
</p>
</div>
);
}
export default App;
// ./App.test.js
import { render, screen } from '@testing-library/react';
import App from './App';
import * as utils from "./demo/utils";
jest.spyOn(utils, "getData").mockReturnValue("mocked message"); // this doesn't work as intended
describe('App', () => {
test('renders header', () => {
render(<App />);
expect(screen.getByText(/My app/i)).toBeInTheDocument()
});
test('renders correct welcome message', () => {
render(<App />)
expect(screen.getByText(/mocked message/i)).toBeInTheDocument()
});
})
jest.spyOn
in beforeEach
Wrap jest.spyOn
in beforeEach
block
beforeEach(() => {
jest.spyOn(utils, "getData").mockReturnValue("mocked message");
});
Now tests should work correctly. This is similar to this Stack Overflow post.
jest.mock
Instead of using import * as ...
we can mock our module with jest.mock
. Following test works fine:
import { render, screen } from '@testing-library/react';
import App from './App';
jest.mock('./demo/utils', () => ({
getData: () => 'mocked message'
}));
describe('App', () => {
test('renders header', () => {
render(<App />);
expect(screen.getByText(/My app/i)).toBeInTheDocument()
});
test('renders correct welcome message', () => {
render(<App />)
expect(screen.getByText(/mocked message/i)).toBeInTheDocument()
});
})
This approach is harder to use if we want to have multiple mocked implementations, e.g. one for testing failing cases and one for normal cases. We would need to use doMock
and async imports.
Upvotes: 23
Reputation: 998
I got the same question and finally I find the solution. I post it here for the one who got the same issue.
Jest test file:
import * as utils from "./demo/utils";
import { welcomeMessage } from "./demo/login";
// option 1: mocked value
const mockGetData = jest.spyOn(utils, "getData").mockReturnValue("hello");
// option 2: mocked function
const mockGetData = jest.spyOn(utils, "getData").mockImplementation((name) => "Hello !!! " + name);
describe("User actions", () => {
it("should get username", () => {
const value = "Hello !!! Jest";
expect(welcomeMessage("Jest")).toEqual(value);
});
});
references: https://jestjs.io/docs/jest-object#jestfnimplementation
jest.spyOn(object, methodName)
Creates a mock function similar to jest.fn but also tracks calls to object[methodName]. Returns a Jest mock function.
Note: By default, jest.spyOn also calls the spied method. This is different behavior from most other test libraries. If you want to override the original function, you can use:
jest.spyOn(object, methodName).mockImplementation(() => customImplementation)
or
object[methodName] = jest.fn(() => customImplementation);
Upvotes: 36
Reputation: 2932
The first argument of jest.mock(...)
must be a module path:
jest.mock('../../src/utils');
because the utils
module is your code, not a 3rd lib, so you must learn manual mock of jest:
https://facebook.github.io/jest/docs/en/manual-mocks.html
if you had this file: src/utils.js
you can mock it by creating a file: src/__mocks__/utils.js
content of this file is the replication of the original but replace the implementation by getData = jest.fn()
on you test file, just call: jest.mock('../../src/utils');
at begin of file.
then when you're familiar with, you can call that function inside beforeEach()
and call its counter jest.unmock('../../src/utils');
insider afterEach()
An easy way to think about it is that:
when you call jest.mock('../../src/utils');
, it means you tell jest
that:
hey if the running test meets the line
require('../../src/utils')
, don't load it, let load../../src/__mocks__/utils
.
Upvotes: 43
Reputation: 1575
another solution would fake this by doing something like:
window['getData'] = jest.fn();
Upvotes: 1