【问题标题】:Create manual mock for @material-ui withStyles in React with Jest在 React with Jest 中为 @material-ui withStyles 创建手动模拟
【发布时间】:2019-07-12 00:43:02
【问题描述】:

我正在尝试为 @material-ui/core/styles 模块创建手动模拟,该模块导出名为 withStyles 的 HOC。

使用带有jest.mock 的内联模拟效果很好,但是当我尝试将此逻辑移动到可共享的__mocks__ 文件夹中时,它不再起作用了。

我从正在测试的文件中删除了所有不必要的代码,只保留了造成问题的两行:

import { withStyles } from '@material-ui/core/styles';

console.log('loaded styles:', withStyles);

export default 'hello';

并且测试也简化了,只是为了看看模拟是否工作,如下:

import test from '../test';
console.log(test);

我尝试的是在项目的根目录中创建一个__mocks__ 文件夹,其中我有node_modules 文件夹。

在那里,我创建了一个名为 @material-ui 的第一个文件夹,并在其中创建了另一个名为 core 的文件夹。 在这个文件夹中,我有一个名为 styles.js 的文件,代码如下:

export const withStyles = 'hello world';

所以结构看起来像:

- __mocks__
  - @material-ui
    - core
      - styles.js
- node_modules

这是有效的代码,在同一个测试文件中定义了模拟:

jest.mock('@material-ui/core/styles', () => ({
  withStyles: () => Component => props => (
    <Component
      classes=""
      {...props}
    />
  ),
}));

__mocks__ 文件夹发生的情况是,如果我不调用任何文件夹

jest.mock('@material-ui/core/styles');

在我的测试文件中,它使用了真正的withStyles,所以是真正的模块。 根本没有使用任何模拟。

如果我使用:

jest.mock('@material-ui/core/styles');

它使用由 jest 自动生成的自动模拟(或者看起来是这样),跳过了我在上面定义的模拟。

只是关于软件包的说明,我使用 CRA 引导应用程序,我目前拥有的 jest 版本是 20,react-scripts 版本是 1.0.17

感谢大家的帮助!

【问题讨论】:

  • 我找到了在 (__mocks__/@material-ui/core/styles.js) 中手动模拟 makeStyles 的完美方法,方法是使用与在旅途中创建的主题绑定的相同原始 makeStyles。这解决了来自undefined theme、测试覆盖、在makeStyles 函数属性中通过props 的未发现测试的所有问题。如果你仍然想要这个,我会花点时间来回答它,因为它还处理 jest.spyOn(React, 'useContext').mockImplementation(() =&gt; {}),它会导致一些 useStyles 崩溃,这些 React.useContext 也在内部使用。
  • withStyles类似,但是makeStyles更适合Hook,我用的就是这个。但同样的逻辑也适用。
  • 另外,你不应该在手动模拟中使用jest.mock('@material-ui/core/styles');。在 Manual Mock 中,您必须返回任何您想要的内容来代替 styles。例如,您可以只导入所有样式,准备一个 withStyles 函数并返回 { ....Styles. withStyles } 以替换 withStyles

标签: javascript reactjs mocking jestjs material-ui


【解决方案1】:

__mocks__/@material-ui/core/styles.js 中,您应该如何使用手动模拟:

// Grab the original exports
import * as Styles from '@material-ui/core/styles';

const mockWithStyles = () => {
  console.log('withStyles being mocked'); // this shows that it works
  /**
   * 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
   */

  return () => {}; // your mock function (adjust as needed)
  // you can also return the same implementation on certain conditions
  // return Styles.makeStyles; 
};

module.exports = { ...Styles, withStyles: mockWithStyles };

这是我手动模拟makeStyles的方法:

// 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';

const mockMakeStyles = 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
   * binding it with our custom theme
   */
  const theme = createMuiTheme(options);
  return Styles.makeStyles(func.bind(null, theme));
};

module.exports = { ...Styles, makeStyles: mockMakeStyles };

makeStyles 结果使用React.useContext,所以我们必须避免为makeStyles 模拟useContext。如果您在组件中首先使用React.useContext(...),请使用mockImplementationOnce,或者只是在您的测试代码中将其过滤掉:

jest.spyOn(React, 'useContext').mockImplementation(context => {
  console.log(context);
  // only stub the response if
  if (context.displayName === 'MyAppContext') {
    return {
      auth: {},
      lang: 'en',
      snackbar: () => {},
    };
  }

  const ActualReact = jest.requireActual('react');
  return ActualReact.useContext(context);
});

在您的createContext() 调用中,可能在store.js 中,添加displayName(标准)或任何其他自定义属性来识别您的上下文:

const store = React.createContext(initialState);
store.displayName = 'MyAppContext';

makeStyles 上下文 displayName 将显示为 StylesContextThemeContext,它们的实现将保持不变以避免错误。

这解决了makeStyles 的各种模拟问题。但是我没有用withStyles深入了解它的结构,但类似的逻辑应该是适用的。

【讨论】:

    【解决方案2】:

    我在我的项目中创建了文件src/__mocks__/@material-ui/core/styles.jsx,并在里面放了:

    import * as Styles from '@material-ui/core/styles';
    import React from 'react';
    
    const withStyles = () => Component => props => <Component classes={{}} {...props} />;
    
    module.exports = { ...Styles, withStyles };
    
    

    【讨论】:

      猜你喜欢
      • 2020-08-30
      • 2023-03-07
      • 1970-01-01
      • 2018-04-11
      • 2019-10-06
      • 2019-10-04
      • 2019-11-29
      • 2019-06-14
      • 2021-05-22
      相关资源
      最近更新 更多