How to test for useEffect inside a custom hook?
So I'm pretty new to testing with react. I have a custom hook that I am calling inside a component. I am using the renderHook
methods from react-hook-testing-library
.
I need to test if the methods inside useEffect
inside the custom hook are called. I cant seem to figure this out.
In another case, I need to figure out if trackPdpGtm
is not called.
Note: The hook does not return any data. This is mainly for sending analytics information.
Current Approach:
usePdpGtm.js
import { useEffect } from 'react';
import { trackPdpGtm } from 'utils/gtm/pdpGtmUtils';
import _ from 'utils/lodashImports';
export default function useGtmPdp(data = {}) {
console.log('are you getting called?');
const { relatedProducts, results, priceInfo, breadcrumbs } = data;
const relatedProductsLoaded = _.get(relatedProducts, 'relatedProductsLoaded');
useEffect(() => {
if (relatedProductsLoaded) {
trackPdpGtm(data);
}
}, [relatedProductsLoaded, results, priceInfo, breadcrumbs]);
}
I need to test if trackPdpGtm
are called. Also need to check that its not called in another test case.
gtm.test.js
import { renderHook, cleanup, act } from 'react-hooks-testing-library';
import usePdpGtm from 'utils/hooks/gtmHooks/usePdpGtm';
import useListPageGtm from 'utils/hooks/gtmHooks/useListPageGtm';
import { trackPdpGtm } from 'utils/gtm/pdpGtmUtils';
import { trackListPageGtm } from 'utils/gtm/plpGtmUtils';
import { mount, shallow } from 'enzyme';
import { ProductDescriptionComponent } from 'components/business/ProductDescription/ProductDescriptionComponent';
jest.mock('utils/hooks/gtmHooks/usePdpGtm');
jest.mock('utils/hooks/gtmHooks/useListPageGtm');
jest.mock('utils/gtm/pdpGtmUtils');
jest.mock('utils/gtm/plpGtmUtils');
trackPdpGtm.mockImplementation = jest.fn();
// ALSO TRIED trackPdpGtm.mockImplementation(() => jest.fn());
trackListPageGtm.mockImplementation(() => console.log('adadada'));
describe('analytics helper', () => {
afterEach(cleanup);
it('should fire usePdpGtm Hook', async (done) => {
const pdpData = {
relatedProducts: {
collectionProducts: [],
relatedProductsLoaded: true
},
productInfo: {
variants: [{}],
breadcrumbs: [{}],
serviceWarrantyDetails: {
services: [],
warranties: [{}]
}
},
priceInfo: [{}],
breadcrumbs: [{}]
};
const { result } = renderHook(() => usePdpGtm(pdpData));
//THIS IS FAILING
expect(trackPdpGtm).toHaveBeenCalledTimes(1);
expect(result.current).toBeUndefined();
});
it('should fire usePdpGtm Hook without data', () => {
const { result } = renderHook(() => usePdpGtm(undefined));
// NEED TO TEST IF trackPdpGtm is NOT called. (also failing)
expect(trackPdpGtm).toNotBeCalled();
expect(result.current).toBeUndefined();
});
});
Also tried using trackGtmPdp.mock.calls.length.toBe(1)
.
The usePdpGtm
hook is called inside ProductDescriptionComponent
and receives an object as its argument.
Note: The custom hook is not running during the test. I am not sure but the console.log statements inside are not printed while the test is running.
Any help is highly appreciated.
1 answer
-
answered 2021-01-14 08:42
slideshowp2
You forget to clear the
mock.calls
andmock.instances
properties oftrackPdpGtm
function mock. That's why your second test case fails. You can use jest.clearAllMocks() to clear it inafterEach
hook.E.g.
usePdpGtm.ts
:// @ts-nocheck import { useEffect } from 'react'; import { trackPdpGtm } from './pdpGtmUtils'; import _ from 'lodash'; export default function useGtmPdp(data = {}) { console.log('are you getting called?'); const { relatedProducts, results, priceInfo, breadcrumbs } = data; const relatedProductsLoaded = _.get(relatedProducts, 'relatedProductsLoaded'); useEffect(() => { if (relatedProductsLoaded) { trackPdpGtm(data); } }, [relatedProductsLoaded, results, priceInfo, breadcrumbs]); }
pdpGtmUtils.ts
:export function trackPdpGtm(data) { console.log('real track pdp gtm implementation'); }
usePdpGtm.test.ts
:import { renderHook, cleanup } from '@testing-library/react-hooks'; import usePdpGtm from './usePdpGtm'; import { trackPdpGtm } from './pdpGtmUtils'; jest.mock('./pdpGtmUtils'); describe('analytics helper', () => { afterEach(() => { jest.clearAllMocks(); cleanup(); }); it('should fire usePdpGtm Hook', () => { const pdpData = { relatedProducts: { collectionProducts: [], relatedProductsLoaded: true, }, productInfo: { variants: [{}], breadcrumbs: [{}], serviceWarrantyDetails: { services: [], warranties: [{}], }, }, priceInfo: [{}], breadcrumbs: [{}], }; const { result } = renderHook(() => usePdpGtm(pdpData)); expect(trackPdpGtm).toHaveBeenCalledTimes(1); expect(result.current).toBeUndefined(); }); it('should fire usePdpGtm Hook without data', () => { const { result } = renderHook(() => usePdpGtm(undefined)); expect(trackPdpGtm).not.toBeCalled(); expect(result.current).toBeUndefined(); }); });
unit test result:
PASS examples/65703648/usePdpGtm.test.ts analytics helper ✓ should fire usePdpGtm Hook (28 ms) ✓ should fire usePdpGtm Hook without data (4 ms) console.log are you getting called? at Object.useGtmPdp [as default] (examples/65703648/usePdpGtm.ts:7:11) console.log are you getting called? at Object.useGtmPdp [as default] (examples/65703648/usePdpGtm.ts:7:11) ----------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s ----------------|---------|----------|---------|---------|------------------- All files | 91.67 | 100 | 66.67 | 91.67 | pdpGtmUtils.ts | 50 | 100 | 0 | 50 | 2 usePdpGtm.ts | 100 | 100 | 100 | 100 | ----------------|---------|----------|---------|---------|------------------- Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 5.167 s