javascript - 如何为 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} />}
)
}
我正在使用这个父组件来显示用户可以在屏幕上查看的价格范围和其他过滤器等过滤器,并为它们添加测试用例。我为此创建了一个子组件 MoreFiltersComponent,以显示许多其他过滤器,用户可以在单击“更多过滤器”按钮后查看这些过滤器。便利设施是 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)} />
)
}
这是我创建的测试用例,用于测试运行良好的价格范围过滤器
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 中的代码行未被发现。有没有办法我也可以覆盖这些代码行?
谢谢!
解决方案
首先,我认为你最好不要嘲笑useState
and useEffect
。这绝对是实现细节,虽然你可以在技术上做到这一点 - 你可能已经达到了这一点 - 使任何更改越来越难以包含在测试中。并不是说如果您出于任何原因决定重塑您的状态(例如,使其中的一部分来自道具或上下文),您将需要完全重写您的测试。这样不好。
其次,因为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)
你也很可能需要act()
from react/test-utils
。一些酶的助手喜欢simulate()
已经调用了,而其他可能没有。此方法确保所有相关的useEffect
/useLayoutEffect
将同步运行和运行。没有act()
你可能会发现在测试完成后useEffect
执行没有任何意义。
牢记这一切,我们可以测试如何FilterComponent
使用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我无法回答你如何用嘲笑useEffect
和useState
和shallow()
推荐阅读
- scala - 如何使用 transformWith() 忽略失败的期货?
- python - 在窗口的下部绘制 PyQt5
- javascript - 使用 docx 节点库添加更多图像以生成 word 文档时出现内存空间问题
- python - 如何查看一个值是否在熊猫的 1 列中有 2 个匹配项
- google-cloud-platform - Google Cloud Run 一遍又一遍地错误运行
- sql - 我可以将此 SQL 代码重写为 querydsl 吗?
- r - R 降价和获取包的问题
- tls1.2 - 使用 tls-crypt 时如何从 Chromebook 连接到 OpenVPN?
- autodesk-forge - 在 Autodesk 查看器中使用 dbids 在对象上绘制标记
- api - 如何在flutter中向graphql API发送post请求