在React with Jest

时间:2019-02-18 15:29:46

标签: javascript reactjs mocking jestjs material-ui

我正在尝试为@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

感谢大家的帮助!

1 个答案:

答案 0 :(得分:0)

以下是您应该如何在 __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,因此我们必须避免为 useContext 模拟 makeStyles。如果您首先在组件中使用 mockImplementationOnce,请使用 React.useContext(...),或者只是在测试代码中将其过滤为:

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 上下文显示名称将显示为 StylesContextThemeContext,并且它们的实现将保持不变以避免错误。

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