如何为React useEffect中的代码编写一个Jest-enzyme测试用例,其依赖关系在其子组件中更新



我有一个过滤功能的父组件

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} />}
)
}

我使用这个父组件来显示过滤器,比如Price range和其他用户可以在屏幕上查看的过滤器,并为它们添加测试用例。我为此创建了一个子组件MoreFiltersComponent,以显示许多其他过滤器,用户可以在单击More filters按钮后查看这些过滤器。便利设施是MoreFilters的一部分,但是我在父组件中为它创建了state,并将它作为props传递,这样我就可以运行父组件的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)} />
)
}

这是我创建的测试用例,用于测试价格范围过滤器是否正常工作

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();
}

我尝试了一个类似的便利设施测试案例,但它不起作用。我不能模拟onChange事件,因为元素不在这个组件中,所以useEffect中我基于便利条件进行过滤的代码行被覆盖了。有没有办法把这些行代码也写进去?

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"]);
}

我得到这个错误的设施测试用例:

预期:["ac","tv","wifi"]

呼叫数:0

我不能模拟onChange事件,因为元素不在这个组件中,所以useEffect中我基于便利条件进行过滤的代码行被覆盖了。有没有办法把这些行代码也写进去?

谢谢!

首先,我认为你最好不要嘲笑useStateuseEffect。它绝对是实现细节,虽然您可以在技术上做到这一点,但您将-并且可能已经达到了这一点-使任何更改越来越难以包含在测试中。没有告诉您,如果您出于任何原因决定重塑您的状态(例如,让它的某些部分来自道具或上下文),您将需要完全重写您的测试。这可不好。

第二,因为shallow()仍然不能与useEffect一起工作。所以就用mount()。是的,这意味着你将有所有嵌套的组件渲染在深度…直到你明确地嘲笑他们!看到:

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) 

你很可能需要从react/test-utils中获取act()。一些酶的助手,比如simulate(),已经这样做了,而其他的可能还没有。此方法确保所有相关的useEffect/useLayoutEffect将以同步方式运行。如果没有act(),您可能会发现在测试完成后执行useEffect,这没有任何意义。

记住所有这些,我们可以测试FilterComponent如何与MoreFiltersComponent一起工作(或者更准确地说与它的mock一起工作):

const wrapper = mount(<FilterComponent data={mockedData} />);
act(() => {
wrapper.find(MoreFiltersComponent).invoke('setAmenities')(['a','b']);
});
expect(wrapper).toMatchSnapshot(); // should be only those matched filters

PS我无法回答你如何用模拟useEffectuseStateshallow()来解决这个任务

相关内容

最新更新