我正在尝试为我的React组件添加更好的测试范围,而我无法嘲笑的地方之一就是它的内部
export const useTabStyles = makeStyles(({ options: { common } }) => ({
>>> root: ({ size }: TabProps) => ({
'&&': {
fontSize: size === 'MD' ? common.fonts.sizes.p3 : common.fonts.sizes.p,
},
}),
}));
当我检查代码覆盖率时,是说未检查>>>
行。
我尝试过这样的事情
jest.mock('@material-ui/core/styles', () => ({
...jest.requireActual('@material-ui/core/styles'),
makeStyles: jest.fn().mockReturnValue(jest.fn()),
}));
但是我不确定如何检查给定的行是否用size = MD or LG
调用。
这是it
it('should render normal style', () => {
wrapper = shallow(<Tab size="MD" />);
// how do I mock check here whtehr the makeStyles received the proepr size.
});
答案 0 :(得分:0)
将其提取到函数中并分别进行测试是什么?
答案 1 :(得分:0)
覆盖范围方面正在发生的事情是正在测试的函数,钩子useTabStyles
是makeStyles
fn的结果,它接受一个回调作为输入,这是缺少覆盖范围的回调,因为它不会在您的模拟之后执行。
如果您以这种方式更改了模拟程序,则还应该执行该代码,然后将其覆盖:
makeStyles: jest.fn().mockImplementation(callback => {
callback({ options: { common: { fonts: { sizes: {} } } } }); // this will execute the fn passed in which is missing the coverage
return jest.fn().mockReturnValue({ // here the expected MUI styles });
}),
您也可以直接在以下行之前添加该fn的覆盖范围检查:
/* istanbul ignore next */
export const useTabStyles = makeStyles(({ options: { common } }) => ({
root: ({ size }: TabProps) => ({
'&&': {
fontSize: size === 'MD' ? common.fonts.sizes.p3 : common.fonts.sizes.p,
},
}),
}));
答案 2 :(得分:0)
用简单的玩笑函数模拟 makeStyles
会让你失去测试覆盖率。当它变得更复杂时,它会导致一些问题,每个解决的问题都会导致另一个问题:
const useStyles = makeStyles(theme => {...})
)useStyles({ variant: 'contained', palette: 'secondary' })
(makeStyles 的结果函数)时传递参数,则会丢失覆盖率(useStyles 参数处理示例)
{
backgroundColor: props => {
if (props.variant === 'contained') {
return theme.palette[props.palette].main;
}
return 'unset';
},
}
我设法解决了所有这些问题并使用了手动模拟 https://jestjs.io/docs/en/manual-mocks:
第 1 步:
我在核心路径中进行了模拟,但两者都应该有效:<root>/__mocks__/@material-ui/core/styles.js
// Grab the original exports
// eslint-disable-next-line import/no-extraneous-dependencies
import * as Styles from '@material-ui/core/styles';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import options from '../../../src/themes/options'; // I put the theme options separately to be reusable
const makeStyles = func => {
/**
* Note: if you want to mock this return value to be
* different within a test suite then use
* the pattern defined here:
* https://jestjs.io/docs/en/manual-mocks
*/
/**
* Work around because Shallow rendering does not
* Hook context and some other hook features.
* `makeStyles` accept a function as argument (func)
* and that function accept a theme as argument
* so we can take that same function, passing it as
* parameter to the original makeStyles and
* bind it to our custom theme, created on the go
* so that createMuiTheme can be ready
*/
const theme = createMuiTheme(options);
return Styles.makeStyles(func.bind(null, theme));
};
module.exports = { ...Styles, makeStyles };
所以基本上,这只是使用相同的原始 makeStyles
并将未按时准备好的自定义主题传递给它。
第 2 步:
makeStyles
结果使用 React.useContext
,因此我们必须避免在 makeStyles 用例中模拟 useContext
。如果您在组件中首先使用 React.useContext(...)
,请使用 mockImplementationOnce,或者最好在测试代码中将其过滤为:
jest.spyOn(React, 'useContext').mockImplementation(context => {
// only stub the response if it is one of your Context
if (context.displayName === 'MyAppContext') {
return {
auth: {},
lang: 'en',
snackbar: () => {},
};
}
// continue to use original useContext for the rest use cases
const ActualReact = jest.requireActual('react');
return ActualReact.useContext(context);
});
在您的 createContext()
调用中,可能在 store.js
中,添加 displayName 属性(标准)或任何其他自定义属性来识别您的上下文:
const store = React.createContext(initialState);
store.displayName = 'MyAppContext';
如果您记录它们,makeStyles 上下文 displayName 将显示为 StylesContext 和 ThemeContext,并且它们的实现将保持不变以避免错误。
这修复了所有与 makeStyles + useContext 相关的模拟问题。在速度方面,感觉就像正常的 shallow
渲染速度,并且在大多数用例中可以让您远离 mount
。
步骤 1 的替代方法:
我们可以在任何测试中使用普通的 jest.mock
,而不是全局手动模拟。这是实现:
jest.mock('@material-ui/core/styles', () => {
const Styles = jest.requireActual('@material-ui/core/styles');
const createMuiTheme = jest.requireActual(
'@material-ui/core/styles/createMuiTheme'
).default;
const options = jest.requireActual('../../../src/themes/options').default;
return {
...Styles,
makeStyles: func => {
const theme = createMuiTheme(options);
return Styles.makeStyles(func.bind(null, theme));
},
};
});
从那以后,我也学会了模拟 useEffect
和调用回调、axios 全局拦截器等
答案 3 :(得分:0)
我遇到了同样的问题,所以我就这样解决了这个问题,希望对其他人也有帮助。
谢谢
这是我的样式文件
import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles((theme) => ({
root:{
backgroundColor: theme.common.white,
}
}));
这是我的组件
import { useStyles } from './ExampleStyles';
const Example = ({ children }) => {
const classes = useStyles();
return (
<div className={classes.root}><h4>Hello world!</h4></div>
);
};
export default Example;
现在是测试用例。
import { ThemeProvider } from '@material-ui/core/styles';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
import Enzyme, { mount } from 'enzyme';
import renderer from 'react-test-renderer';
import Example from 'shared/components/Example';
import theme from 'shared/utils/theme';
Enzyme.configure({ adapter: new Adapter() });
describe('Example Component', () => {
const props = {};
it('Should render Example component', () => {
const wrapper = mount(
<ThemeProvider theme={theme}>
<Example {...props} />
</ThemeProvider>
);
expect(wrapper).toBeTruthy();
});
it('Example Component snapshot testing', () => {
const div = document.createElement('div');
const tree = renderer
.create(
<ThemeProvider theme={theme}>
<Example {...props} />
</ThemeProvider>,
div
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
我只是使用 ThemeProvider 来访问 html 中的主题变量