[英]How to write a Jest-enzyme test case for code in React useEffect whose dependencies are updated in its child component
I have a parent component for filtering functionality我有一个用于过滤功能的父组件
export const FilterComponent = ({data}) {
const [priceRange, setPriceRange] = React.useState({});
const [amenities, setAmenities] = React.useState([]);
React.useEffect(() => {
if (priceRange.min) {
// code to filter based on priceRange
}
if(amenities.length) {
// code to filter based on amenities
},[priceRange, amenities])
return (
<select id="price" value={priceRange} onChange={(event) => setPriceRange(JSON.parse(event.target.value))}>
<option value="">Price Per Night</option>
<option value='{"min":0,"max":99}'>Less than $99</option>
<option value='{"min":100,"max":299}'>$100 to $299</option>
<option value='{"min":300,"max":499}'>$300 to $499</option>
<option value='{"min":500}'>More than $500</option>
</select>
<MoreFiltersComponent setAmenities={setAmenities} amenities={amenities} />}
)
}
I am using this parent component to display the filters like Price range and other filters that user can view on the screen and added test cases for them.我使用这个父组件来显示过滤器,比如价格范围和用户可以在屏幕上查看的其他过滤器,并为它们添加了测试用例。 I created a child component to this, MoreFiltersComponent, to display many other filters which user can view after clicking on More Filters button.
我为此创建了一个子组件,MoreFiltersComponent,以显示许多其他过滤器,用户可以在单击“更多过滤器”按钮后查看这些过滤器。 amenities is part of MoreFilters but I created state for it in parent component and passing it as props so that I can run useEffect of parent component to check all the filters when there is any update.
便利设施是 MoreFilters 的一部分,但我在父组件中为它创建了状态并将其作为道具传递,以便我可以在有任何更新时运行父组件的 useEffect 来检查所有过滤器。
export const MoreFiltersComponent = ({setAmenities, amenities}) => {
const updateValue = (event, state, updateState) => {
// code to update the state
}
return (
<input type="checkbox" name="ac" id="ac" value="ac" onChange={(event) => updateValue(event, amenities, setAmenities)} />
)
}
This is the test case I created to test the price range filter which is working fine这是我创建的测试用例,用于测试运行良好的价格范围过滤器
const setPriceRangeSpy = jest.fn();
const mockUseEffect = () => {
useEffect.mockImplementationOnce((f) => f());
};
it('should render the correct results for selected price range', () => {
const event = {
target: { value: '{"min": 0, "max": 99}' },
};
useStateSpy.mockImplementationOnce(() => [{}, setPriceRangeSpy]);
renderedModule = shallow(<FilterComponent {...props} />);
mockUseEffect();
renderedModule.find('#price').simulate('change', event);
expect(setPriceRangeSpy).toHaveBeenCalledWith({ min: 0, max: 99 });
expect(renderedModule).toMatchSnapshot();
}
I tried a similar test case for amenities but it doesn't work.我尝试了一个类似的便利设施测试用例,但它不起作用。 I can't simulate the onChange event as the element isn't in this component and so the lines of code in useEffect where I filter based on amenities are uncovered.
我无法模拟 onChange 事件,因为该元素不在此组件中,因此 useEffect 中的代码行在我根据便利条件进行过滤的地方没有被发现。 is there a way I can cover those lines of code as well ?
有没有办法我也可以覆盖这些代码行?
const setAmenitiesSpy = jest.fn();
const mockUseEffect = () => {
useEffect.mockImplementationOnce((f) => f());
};
it('should render the correct results when there is no max price', () => {
useStateSpy.mockImplementationOnce(() => [["ac", "tv", "wifi"], setAmenitiesSpy]);
renderedModule = shallow(<FilterComponent {...props} />);
mockUseEffect();
expect(setAmenitiesSpy).toHaveBeenCalledWith(["ac", "tv", "wifi"]);
}
I get this error for amenities test case:对于设施测试用例,我收到此错误:
Expected: ["ac", "tv", "wifi"]预期:[“交流”,“电视”,“无线网络”]
Number of calls: 0通话次数:0
I can't simulate the onChange event as the element isn't in this component and so the lines of code in useEffect where I filter based on amenities are uncovered.我无法模拟 onChange 事件,因为该元素不在此组件中,因此 useEffect 中的代码行在我根据便利条件进行过滤的地方没有被发现。 is there a way I can cover those lines of code as well ?
有没有办法我也可以覆盖这些代码行?
Thanks!谢谢!
First, I think you better not mocking useState
and useEffect
.首先,我认为你最好不要嘲笑
useState
和useEffect
。 It's definitely implementation details and while you can technically do that you will - and you probably reached this point - make any change harder and harder to include in test.这绝对是实现细节,虽然您在技术上可以做到这一点 - 您可能已经达到了这一点 - 使任何更改越来越难以包含在测试中。 Not telling that if you for any reason ever decide to reshape your state(say, making some part of it coming from props or context instead) you will need to completely rewrite your tests.
不告诉你,如果你出于任何原因决定重塑你的状态(比如,让它的一部分来自 props 或上下文),你将需要完全重写你的测试。 It's not good.
这不好。
Second, since shallow()
still does not work fine with useEffect
.其次,因为
shallow()
仍然不能很好地与useEffect
一起useEffect
。 So just use mount()
.所以只需使用
mount()
。 Yes, it means you will have all nested components to be rendered in deep... until you explicitly mock them!是的,这意味着您将深入渲染所有嵌套组件……直到您明确模拟它们! See:
看:
import { MoreFiltersComponent } from '../path/to/MoreFiltersComponent.jsx';
jest.mock('../path/to/MoreFiltersComponent.jsx', () => ({
MoreFiltersComponent: function MoreFiltersComponent() {
// so this is mocked React component that always returns null
return null;
}
}));
....
const wrapper = mount(.....);
// you still can search for this component, even if it's mocked and always returns null in render
wrapper.find(MoreFiltersComponent)
Also you most probably will need act()
from react/test-utils
.此外,您很可能需要
react/test-utils
act()
。 Some enzyme's helpers like simulate()
already call that while others maybe don't.一些酶的助手(如
simulate()
已经调用了它,而其他酶可能没有调用。 This method ensures all the related useEffect
/ useLayoutEffect
will be run and run in sync way.此方法确保所有相关的
useEffect
/ useLayoutEffect
将以同步方式运行和运行。 Without act()
you may find useEffect
is executed after test has been completed that does not make any sense.如果没有
act()
您可能会发现在测试完成后执行useEffect
没有任何意义。
Keeping all that in mind, we can test how FilterComponent
works with MoreFiltersComponent
(or more precisely with its mock):记住所有这些,我们可以测试
FilterComponent
如何与MoreFiltersComponent
一起MoreFiltersComponent
(或者更准确地说是与其模拟):
const wrapper = mount(<FilterComponent data={mockedData} />);
act(() => {
wrapper.find(MoreFiltersComponent).invoke('setAmenities')(['a','b']);
});
expect(wrapper).toMatchSnapshot(); // should be only those matched filters
PS I cannot answer how you can solve that task with mocked useEffect
and useState
and shallow()
PS 我无法回答你如何用
useEffect
和useState
以及shallow()
来解决这个任务
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.