编辑:我能够确定 MUI 的指令在使用 RTL 时可以正常工作。这个问题只发生在酶测试中!
我正在关注 MUI 关于如何测试 useMediaQuery 的文档,但我很困惑我在组件中使用 useMediaQuery 的方式(在 MUI 文档中概述)是否与 MUI 中的测试说明兼容文档。
这是我组件中的代码:
import { useTheme } from '@mui/material/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
const List = () => {
const theme = useTheme();
const isDownLargeBreakpoint =
useMediaQuery(theme.breakpoints.down('lg'));
...
{isDownLargeBreakpoint && (
<ul className="list">
// list items
</ul>
)}
}
当我在本地运行应用程序时,useMediaQuery 挂钩按预期工作,当我调整 MUI 主题
true
断点下方/上方的屏幕大小时,它会在 false
和 lg
之间正确切换。
当我尝试使用推荐的设置方法运行测试时,尽管 window.innerWidth 低于满足 useMediaQuery 返回值
true
的值,但我在测试中总是得到 false
。也许是因为我没有在测试中重新渲染我的组件?或者我是否必须在 it
子句中做更多事情才能触发需要发生的事情?
这是使用 MUI 推荐的
css-mediaquery
的代码块以及 已经回答的这篇文章:
import mediaQuery from 'css-mediaquery';
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addListener: () => {},
removeListener: () => {},
});
}
describe('MyTests', () => {
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
});
这是我组织测试文件的方式:
import React from 'react';
import { shallow } from 'enzyme';
import mediaQuery from 'css-mediaquery';
import SessionStore from 'app/stores/SessionStore';
import CustomFields from '../CustomFields';
import CustomFieldButton from '../CustomFieldButton';
import PrepareFieldsList from '../PrepareFieldsList';
describe('PrepareFieldsList Component', () => {
let wrapper;
function createMatchMedia(width) {
return (query) => ({
matches: mediaQuery.match(query, {
width,
}),
addListener: () => {},
removeListener: () => {},
});
}
beforeAll(() => {
window.matchMedia = createMatchMedia(window.innerWidth);
});
const defaultProps = {
customFields: [
{
data: null,
id: 'fieldId',
name: '',
required: false,
value: 'test',
},
],
};
beforeEach(() => {
jest.spyOn(SessionStore, 'getSession').mockReturnValue({
hasFeature: () => true,
});
wrapper = shallow(<PrepareFieldsList {...defaultProps} />);
});
...
it('should render CustomFieldButton and CustomFields when hasFeature is true', () => {
expect(wrapper.find(CustomFieldButton)).toHaveLength(1);
expect(wrapper.find(CustomFields)).toHaveLength(1);
});
});
我认为这是因为 innerWidth 没有在 window 中定义,你需要手动设置它的值,所以你可以在 createMatchMedia 中传递值,如:createMatchMedia(1000) 或将值设置为 window.innerWidth,如下所示:
Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 500})
在你的例子中会是这样的:
beforeAll(() => {
Object.defineProperty(window, 'innerWidth', {writable: true, configurable: true, value: 500})
window.matchMedia = createMatchMedia(window.innerWidth);
});
我想通了。在 Enzyme 中执行此操作时,您需要使用 jest 来模拟钩子及其返回值。
在测试文件的顶部:
import useMediaQuery from '@material-ui/core/useMediaQuery';
文件顶部的模拟,导入下方:
jest.mock('@material-ui/core/useMediaQuery');
然后更新模拟值:
useMediaQuery.mockReturnValueOnce(true);
确保在每个测试用例中渲染组件之前执行此操作。所以喜欢:
it('should render ChildComponent when useMediaQuery is true', () => {
useMediaQuery.mockReturnValueOnce(true);
const wrapper = shallow(<ParentComponent />);
expect(wrapper).toContainExactlyOneMatchingElement(ChildComponent);
});
我在花了一些时间诊断我的测试后遇到了这个问题,因为我的自定义断点未被识别,所以该测试失败了。最后,我的答案很简单,只需将正在测试的组件包装在 MUI ThemeProvider 中(传递我的主题)即可。否则,我使用文档中的示例来实现 matchMedia (link)。
<ThemeProvider theme={theme}>
<Component />
<ThemeProvider>