I have got code for usePrevious hook from somewhere on internet. The code for usePrevious looks like:
export const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
Now, I am learing testing react with jest and enzyme. So, I tried to test usePrevious and got some problems. Here is my test case:
import React from 'react';
import { render } from 'enzyme';
import { usePrevious } from './customHooks';
const Component = ({ children, value }) => children(usePrevious(value));
const setup = (value) => {
let returnVal = '';
render(
<Component value={value}>
{
(val) => {
returnVal = val;
return null;
}
}
</Component>,
);
return returnVal;
};
describe('usePrevious', () => {
it('returns something', () => {
const test1 = setup('test');
const test2 = setup(test1);
expect(test2).toBe('test');
});
});
When the test execution completes, I get this error:
Expected: 'test', Received: undefined
Can anyone please let me know why am I getting undefined and is this the correct way to test custom hoooks in react?
After suggestion from comments from @Dmitrii G, I have changed my code to re-render the component (Previously I was re-mounting the component).
Here is the updated code:
import React from 'react';
import PropTypes from 'prop-types';
import { shallow } from 'enzyme';
import { usePrevious } from './customHooks';
const Component = ({ value }) => {
const hookResult = usePrevious(value);
return (
<div>
<span>{hookResult}</span>
<span>{value}</span>
</div>
);
};
Component.propTypes = {
value: PropTypes.string,
};
Component.defaultProps = {
value: '',
};
describe('usePrevious', () => {
it('returns something', () => {
const wrapper = shallow(<Component value="test" />);
console.log('>>>>> first time', wrapper.find('div').childAt(1).text());
expect(wrapper.find('div').childAt(0).text()).toBe('');
// Test second render and effect
wrapper.setProps({ value: 'test2' });
console.log('>>>>> second time', wrapper.find('div').childAt(1).text());
expect(wrapper.find('div').childAt(0).text()).toBe('test');
});
});
But still I am getting the same error
Expected: "test", Received: ""
Tests Passes when settimeout is introduced:
import React from 'react';
import PropTypes from 'prop-types';
import { shallow } from 'enzyme';
import { usePrevious } from './customHooks';
const Component = ({ value }) => {
const hookResult = usePrevious(value);
return <span>{hookResult}</span>;
};
Component.propTypes = {
value: PropTypes.string,
};
Component.defaultProps = {
value: '',
};
describe('usePrevious', () => {
it('returns empty string when component is rendered first time', () => {
const wrapper = shallow(<Component value="test" />);
setTimeout(() => {
expect(wrapper.find('span').text()).toBe('');
}, 0);
});
it('returns previous value when component is re-rendered', () => {
const wrapper = shallow(<Component value="test" />);
wrapper.setProps({ value: 'test2' });
setTimeout(() => {
expect(wrapper.find('span').text()).toBe('test');
}, 0);
});
});
I am not a big fan of using settimeout, so I feel that probably i am doing some mistake. If anyone knows a solution that does not use settimeout, feel free to post here. Thank you.
Enzyme by the hood utilizes React's shallow renderer. And it has issue with running effects . Not sure if it's going to be fixed soon. Workaround with setTimeout
was a surprise to me, did not know it works. Unfortunately, it is not universal approach since you'd need to use that on any change that cases re-render. Really fragile.
As a solution you can use mount()
instead.
Also you may mimic shallow rendering with mount()
with mocking every nested component:
jest.mock("../MySomeComponent.jsx", () =>
(props) => <span {...props}></span>
);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.