how to change jest mock function return value in each test? - reactjs

I have a mock module like this in my component test file
jest.mock('../../../magic/index', () => ({
navigationEnabled: () => true,
guidanceEnabled: () => true
}));
these functions will be called in render function of my component to hide and show some specific feature.
I want to take a snapshot on different combinations of the return value of those mock functions.
for suppose I have a test case like this
it('RowListItem should not render navigation and guidance options', () => {
const wrapper = shallow(
<RowListItem type="regularList" {...props} />
);
expect(enzymeToJson(wrapper)).toMatchSnapshot();
});
to run this test case I want to change the mock module functions return values to false like this dynamically
jest.mock('../../../magic/index', () => ({
navigationEnabled: () => false,
guidanceEnabled: () => false
}));
because i am importing RowListItem component already once so my mock module wont re import again. so it wont change. how can i solve this ?

You can mock the module so it returns spies and import it into your test:
import {navigationEnabled, guidanceEnabled} from '../../../magic/index'
jest.mock('../../../magic/index', () => ({
navigationEnabled: jest.fn(),
guidanceEnabled: jest.fn()
}));
Then later on you can change the actual implementation using mockImplementation
navigationEnabled.mockImplementation(()=> true)
//or
navigationEnabled.mockReturnValueOnce(true);
and in the next test
navigationEnabled.mockImplementation(()=> false)
//or
navigationEnabled.mockReturnValueOnce(false);

what you want to do is
import { navigationEnabled, guidanceEnabled } from '../../../magic/index';
jest.mock('../../../magic/index', () => ({
navigationEnabled: jest.fn(),
guidanceEnabled: jest.fn()
}));
describe('test suite', () => {
it('every test', () => {
navigationEnabled.mockReturnValueOnce(value);
guidanceEnabled.mockReturnValueOnce(value);
});
});
you can look more about these functions here =>https://facebook.github.io/jest/docs/mock-functions.html#mock-return-values

I had a hard time getting the accepted answers to work - my equivalents of navigationEnabled and guidanceEnabled were undefined when I tried to call mockReturnValueOnce on them.
Here's what I had to do:
In ../../../magic/__mocks__/index.js:
export const navigationEnabled = jest.fn();
export const guidanceEnabled = jest.fn();
in my index.test.js file:
jest.mock('../../../magic/index');
import { navigationEnabled, guidanceEnabled } from '../../../magic/index';
import { functionThatReturnsValueOfNavigationEnabled } from 'moduleToTest';
it('is able to mock', () => {
navigationEnabled.mockReturnValueOnce(true);
guidanceEnabled.mockReturnValueOnce(true);
expect(functionThatReturnsValueOfNavigationEnabled()).toBe(true);
});

Related

How to properly mock named export children components with jest

So, here is a simplified version of both my component and my test.
export const VerifyPositionsDialog = () => {
return (
<BaseVerifyPositionsDialog>
<PositionsArea />
</BaseVerifyPositionsDialog>
);
};
I've omitted props and components logic for better readability.
What I've been trying to do is to mock PositionsArea component, so I can unit test VerifyPositionsDialog isolatedly.
This is my test so far.
jest.mock("../VerifyPositionsDialog/PositionsArea", () => ({
__esModule: true,
PositionsArea: () => <div />,
}));
render(<VerifyPositionsDialog />);
I've already tried a lot of different ways based on other answer from SO, but none seems to work.
Any help would be awesome.
You should mock the component using jest.fn returning the mocked div:
jest.mock('../VerifyPositionsDialog/PositionsArea', () =>
jest.fn(() => <div>Mocked</div>),
);
describe('Test', () => {
it('Mock Component Test', () => {
const { debug } = render(<VerifyPositionsDialog />);
// You will check with debug() that mocked div was rendered
debug();
});
});

How do you mock isPlatform from #ionic/core

Have a React Ionic app where I need to determine if the app is running on an iOS device. I've done that by importing isPlatform.
import { isPlatform } from "#ionic/core";
const isIOS = isPlatform("ios");
I've tried the following and when isPlatform is called it still returns false.
jest.mock("#ionic/core", () => ({
isPlatform: () => {
return true;
}
}));
How do I mock isPlatform in my unit test using jest so it returns true?
Figured it out. I needed to mock ionic/core in order for it to work.
jest.mock("#ionic/core");
import * as Ionic from '#ionic/core';
(Ionic as any).isPlatform = jest.fn(() => true);
If component only uses isFlatform in #ionic/core you can mock one function isFlatform:
jest.mock("#ionic/core", () => ({
isPlatform: () => true,
}));
but when component use another function and you only want to mock isFlatform you can use:
jest.mock("#ionic/core", () => ({
...jest.requireActual("#ionic/core"),
isPlatform: () => true,
}));
Similar to the previous responses but using a slightly more flexible approach to be able to mock the result based on what we want to test in each scenario. In our app we're using Angular but this approach should be working as well for React.
The idea is to define a mockIsPlatform() function:
// Note: on our app, we're importing the `isPlatform` helper method
// from `#ionic/angular` but that's not really important.
let mockIsPlatform: (key: string) => boolean;
jest.mock('#ionic/angular', () => ({
...(jest.requireActual('#ionic/angular') as object),
isPlatform: (key: string) => mockIsPlatform(key),
}));
Our service has some methods that use the isPlatform() method behind the scenes:
public isIos(): boolean {
return isPlatform('ios');
}
public isAndroid(): boolean {
return isPlatform('android');
}
So we can now test those methods like this:
test('detect platform', () => {
// Simulate ios platform
mockIsPlatform = (key) => key === 'ios';
expect(myServiceInstance.isIos()).toBeTruthy();
expect(myServiceInstance.isAndroid()).toBeFalsy();
// Simulate android platform
mockIsPlatform = (key) => key === 'android';
expect(myServiceInstance.isIos()).toBeFalsy();
expect(myServiceInstance.isAndroid()).toBeTruthy();
})

Test a component with useState and setTimeout

Code structure is as same as given below:
FunctionComponent.js
...
const [open, handler] = useState(false);
setTimeout(() => {handler(true);}, 2000);
...
return (
...
<div className={active ? 'open' : 'close'}>
)
comp.test.js
jest.useFakeTimers();
test('test case 1', () => {
expect(wrapper.find('open').length).toBe(0);
jest.advanceTimersByTime(2000);
expect(wrapper.find('open').length).toBe(1);
jest.useRealTimers();
});
The problem is that the expression written in bold in test is saying the length of open class is still 0, so actual and expected are not meeting.
You want to test the outcome of the hook and not the hook itself since that would be like testing React. You effectively want a test where you check for if the open class exists and then doesn't exist (or vice versa), which it looks like you're trying.
In short, to solve your issue you need to use ".open" when selecting the class. I would also suggest using the .exists() check on the class instead of ".length()" and then you can use ".toBeTruthy()" as well.
You could look into improve writing your tests in a Jest/Enzyme combined format as well:
import { shallow } from 'enzyme';
import { FunctionComponent } from './FunctionComponent.jsx';
jest.useFakeTimers();
describe('<FunctionCompnent />', () => {
const mockProps = { prop1: mockProp1, prop2: mockProp2, funcProp3: jest.fn() };
const wrapper = shallow(<FunctionComponent {...mockProps} />);
afterEach(() => {
jest.advanceTimersByTime(2000);
});
afterAll(() => {
jest.useRealTimers();
});
it('should render as closed initially', () => {
expect(wrapper.find('.close').exists()).toBeTruthy();
// you could also add the check for falsy of open if you wanted
// expect(wrapper.find('.open').exists()).toBeFalsy();
});
it('should change to open after 2 seconds (or more)', () => {
expect(wrapper.find('.open').exists()).toBeTruthy();
// you could also add the check for falsy of close if you wanted
// expect(wrapper.find('.close').exists()).toBeFalsy();
});
});
EDIT: Sorry realised I wrote the test backwards after checking your code again, they should be fixed now.

react-testing-library | Cannot Split Test into smaller chunks inside describe method

I'm learning about unit testing React components using react-testing-library
I have the component rendering correctly, however, when I aim to break the test into smaller chunks inside a describe() function. The test breaks and here's why.
Current only one or the other test() passes but not both
import React from 'react'
import 'react-testing-library/cleanup-after-each'
import { render, fireEvent } from 'react-testing-library'
import Quantity from '../components/Quantity'
describe('Quantity Component', () => {
const { container, getByTestId } = render(<Quantity />)
// first test
test('checks that quantity is never 0', () => {
expect(getByTestId('quantity')).not.toBe('0')
})
// second test
test('checks for the initial product quantity count', () => {
expect(getByTestId('quantity')).toHaveTextContent('1')
fireEvent.click(getByTestId('increment'))
expect(getByTestId('quantity')).toHaveTextContent('2')
})
})
When trying to run both tests it errors:
Unable to find an element by: [data-testid="quantity"]
[data-testid="quantity"] is just an attribute that I passed inside my desired JSX tag.
The test passes when running only the first or second test but not both concurrently.
What am I missing here?
Cross-contamination is strictly discouraged in unit testing.
The problem is that a setup occurs only once per Quantity Component suite, while it should be done for each test. This is what beforeEach is for:
describe('Quantity Component', () => {
let container, getByTestId;
beforeEach(() => {
({ container, getByTestId } = render(<Quantity />));
});
...
You need to also use an afterEach cleanup.
describe('your tests', () => {
afterEach(cleanup);
beforeEach(() => ({container, getById} = render(<Quantity />))
it('does something', () => {
expect(getByTestId('quantity')).toHaveTextContent(0);
}
}
I suggest you call the render inside your it clauses, it keeps the tests easier to manage:
describe('Quantity Component', () => {
test('checks that quantity is never 0', () => {
const { container, getByTestId } = render(<Quantity />)
expect(getByTestId('quantity')).not.toBe('0')
})
test('checks for the initial product quantity count', () => {
const { container, getByTestId } = render(<Quantity />)
expect(getByTestId('quantity')).toHaveTextContent('1')
fireEvent.click(getByTestId('increment'))
expect(getByTestId('quantity')).toHaveTextContent('2')
})
})
The added advantage is that if for some reason one of your tests needs to run with different props you can do that more easily with this setup.

How to spy on a default exported function with Jest?

Suppose I have a simple file exporting a default function:
// UniqueIdGenerator.js
const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
export default uniqueIdGenerator;
Which I would use like this:
import uniqueIdGenerator from './UniqueIdGenerator';
// ...
uniqueIdGenerator();
I want to assert in my test that this method was called while keeping the original functionality. I'd do that with jest.spyOn however, it requires an object as well as a function name as parameters. How can you do this in a clean way? There's a similar GitHub issue for jasmine for anyone interested.
I ended up ditching the default export:
// UniqueIdGenerator.js
export const uniqueIdGenerator = () => Math.random().toString(36).substring(2, 8);
And then I could use and spy it like this:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'uniqueIdGenerator');
Some recommend wrapping them in a const object, and exporting that. I suppose you can also use a class for wrapping.
However, if you can't modify the class there's still a (not-so-nice) solution:
import * as UniqueIdGenerator from './UniqueIdGenerator';
// ...
const spy = jest.spyOn(UniqueIdGenerator, 'default');
one could also mock the import and pass the original implementation as mock implementation, like:
import uniqueIdGenerator from './UniqueIdGenerator'; // this import is a mock already
jest.mock('./UniqueIdGenerator.js', () => {
const original = jest. requireActual('./UniqueIdGenerator')
return {
__esModule: true,
default: jest.fn(original.default)
}
})
test(() => {
expect(uniqueIdGenerator).toHaveBeenCalled()
})
Here is a way of doing it for a default export without modifying the import (or even needing an import in the test at all):
const actual = jest.requireActual("./UniqueIdGenerator");
const spy = jest.spyOn(actual, "default");
In some cases you have to mock the import to be able to spy the default export:
import * as fetch from 'node-fetch'
jest.mock('node-fetch', () => ({
default: jest.fn(),
}))
jest.spyOn(fetch, 'default')
Mock only the default export, or any other export, but keep remaining exports in module as original:
import myDefault, { myFunc, notMocked } from "./myModule";
jest.mock("./myModule", () => {
const original = jest.requireActual("./myModule");
return {
__esModule: true,
...original,
default: jest.fn(),
myFunc: jest.fn()
}
});
describe('my description', () => {
it('my test', () => {
myFunc();
myDefault();
expect(myFunct).toHaveBeenCalled();
expect(myDefault).toHaveBeenCalled();
myDefault.mockImplementation(() => 5);
expect(myDefault()).toBe(5);
expect(notMocked()).toBe("i'm not mocked!");
})
});
Use 'default' as the second argument in spyOn function.
import * as MyHelperMethod from '../myHelperMethod';
jest.spyOn(MyHelperMethod, 'default');
What worked for me was a combination of the answer from Janne Annala and OP's own solution. All I wanted to test was that the helper method was called with the correct parameters as I had already written a test for the helper method and it didn't have any bearing on my subsequent test:
// myHelperMethod.js
export const myHelperMethod = (param1, param2) => { // do something with the params };
// someOtherFileUsingMyHelperMethod.js
import * as MyHelperMethod from '../myHelperMethod';
jest.mock('../myHelperMethod', () => ({
myHelperMethod: jest.fn(),
}));
let myHelperMethodSpy = jest.spyOn(MyHelperMethod, 'myHelperMethod');
// ...
// some setup
// ...
test(() => {
expect(myHelperMethodSpy).toHaveBeenCalledWith(param1, param2);
});
Here it is even simpler.
Mock your exported module 'addDelay' (has the sleep function in it) using jest.
const { sleep } = require('../../src/utils/addDelay');
jest.mock('../../src/utils/addDelay', () => {
const delay = jest.requireActual('../../src/utils/addDelay');
return {
...delay,
sleep: jest.fn(),
};});
And the test is as follows and check if sleep function was called with 1 sec as in arg.
test("Should delay 1 second if Okta user has no IDxM Roles", async () => {
// GIVEN
const MockSleep = sleep;
// WHEN
await getUser(req, res);
// THEN
expect(MockSleep).toHaveBeenCalledWith(1000);// sleep(1000): 1sec
});
I know I'm late to the party but I recently had this problem and wanted to share my solution as well ... though it seems a bit more unconventional but could be tweaked by someone with better knowledge.
I happen to have a file with the function that I would like to spy on.
// /foo/ModuleToBeMocked.ts
const fnToSpyOn = () => ...;
export default { fnToSpyOn }
This is then imported into a parent file that would bring, and export, alike functions. Sort of like a classification.
// /parent.ts
import fnToSpyOn from './foo/ModuleToBeMocked';
import someOtherFn from './foo/SomeOtherModule';
...
export { fnToSpyOn, someOtherFn, ... };
And this is how I test the fnToSpyOn
// /foo/ModuleToBeMocked.test.ts
import { ModuleToBeMocked } from '../parent';
const fnToSpyOnSpu = jest.spyOn(ModuleToBeMocked, 'fnToSpyOn');

Resources