【问题标题】:How to test styled Material-UI components wrapped in withStyles using react-testing-library?如何使用 react-testing-library 测试包装在 withStyles 中的样式化 Material-UI 组件?
【发布时间】:2019-09-20 04:28:56
【问题描述】:

我正在尝试使用 typescript 中的 react-testing-library 创建带有样式的 Material-UI 组件的测试。我发现很难访问组件的内部功能来模拟和断言。

Form.tsx

export const styles = ({ palette, spacing }: Theme) => createStyles({
    root: {
        flexGrow: 1,
    },
    paper: {
        padding: spacing.unit * 2,
        margin: spacing.unit * 2,
        textAlign: 'center',
        color: palette.text.secondary,
    },
    button: {
        margin: spacing.unit * 2,
    }
});

interface Props extends WithStyles<typeof styles> { };

export class ExampleForm extends Component<Props, State> {
  async handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    // Handle form Submit
    ...
    if (errors) {
            window.alert('Some Error occurred');
            return;
        }
  }
  // render the form
}
export default withStyles(styles)(ExampleForm);

Test.tsx

import FormWithStyles from './Form';

it('alerts on submit click', async () => {
  jest.spyOn(window,'alert').mockImplementation(()=>{});
  const spy = jest.spyOn(ActivityCreateStyles,'handleSubmit');
  const { getByText, getByTestId } = render(<FormWithStyles />)
  fireEvent.click(getByText('Submit'));

  expect(spy).toHaveBeenCalledTimes(1);
  expect(window.alert).toHaveBeenCalledTimes(1);
})

jest.spyOn 抛出以下错误Argument of type '"handleSubmit"' is not assignable to parameter of type 'never'.ts(2345) 可能是因为 ExampleForm 包含在 withStyles 中。

我也试过直接导入ExampleForm组件并手动分配样式,还是不行:

import {ExampleForm, styles} from './Form';

it('alerts on submit click', async () => {
  ...

  const { getByText, getByTestId } = render(<ActivityCreateForm classes={styles({palette,spacing})} />)

  ...
}

得到以下错误:Type '{ palette: any; spacing: any; }' is missing the following properties from type 'Theme': shape, breakpoints, direction, mixins, and 4 more.ts(2345)

由于强类型和包装组件,我发现很难在 Typescript 中为 Material-UI 组件和 react-testing-libraryJest 编写基本测试。请指导。

【问题讨论】:

  • 你能发布这个问题的codeandbox演示吗?

标签: reactjs typescript jestjs material-ui react-testing-library


【解决方案1】:

为什么不将enzymeFull DOM Rendering 一起使用? 您可以使用simulate 方法模拟挂载组件上的事件。

class Foo extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    const { count } = this.state;
    return (
      <div>
        <div className={`clicks-${count}`}>
          {count} clicks
        </div>
        <a href="url" onClick={() => { this.setState({ count: count + 1 }); }}>
          Increment
        </a>
      </div>
    );
  }
}

const wrapper = mount(<Foo />);

expect(wrapper.find('.clicks-0').length).to.equal(1);
wrapper.find('a').simulate('click');
expect(wrapper.find('.clicks-1').length).to.equal(1);

【讨论】:

  • 我希望使用@kentcdodds 的react-testing-library,因为它是一个非臃肿的库,它以我的用户打算使用该应用程序的方式工作。如果没有其他选择,我想我可以切换到enzyme,但我看不出有什么理由不能用react-testing-library 测试Material-UI
  • 会因为handleSubmit方法是async而发生该错误吗?你试过没有async/await吗?
  • 使用组件库进行测试时,我会查看该库中的建议:github.com/mui-org/material-ui/tree/master/test#writing-testsFor all unit tests, please use the shallow renderer from enzyme unless the Component being tested requires a DOM. Here's a small shallow rendered test to get you started.
【解决方案2】:

首先,当您使用react-testing-libraryrender 方法时,您不必担心使用withStyles 或任何包装器,因为最后它会呈现组件,因为它可能在真实的dom 中,所以您可以正常编写测试。

那么据我所知,您正在做的事情与我刚开始测试时所做的相同(这意味着您将变得擅长它;)。您正在尝试模拟一个内部方法,这不是最好的方法,因为您需要做的是测试真正的方法。

让我们想象一下,我们有一个 Register 用户组件。

src/Register.tsx

import ... more cool things
import * as api from './api';

const Register = () => {
  const [name, setName] = useState('');
  const handleNameChange = (event) => {
    setName(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    if (name) {
      api.registerUser({ name });
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <TextField
        id='name'
        name='name'
        label='Name'
        fullWidth
        value={name}
        onChange={handleNameChange}
      />
      <Button data-testid='button' fullWidth type='submit' variant='contained'>
        Save
      </Button>
    </form>
  );
}

该组件非常简单,它是一个带有输入和按钮的表单。我们正在使用react hooks 来更改输入值,并基于我们在触发handleSubmit 事件时调用或不调用api.registerUser

要测试组件,我们需要做的第一件事是模拟 api.registerUser 方法。

src/__tests__/Register.tsx

import * as api from '../api'

jest.mock('../api')

api.registerUser = jest.fn()

这将允许我们查看该方法是否被调用。

接下来要做的是……编写测试,在这种情况下,我们可以测试两件事以查看handleSubmit 是否正常工作。

  1. 如果姓名为空,则不要致电api.registerUser
it('should not call api registerUser method', () => {
  const { getByTestId } = render(<Register />)
  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(0)
})
  1. 如果姓名为空,请致电api.registerUser
it('should call api registerUser method', () => {
  const { getByLabelText, getByTestId } = render(<Register />)

  fireEvent.change(getByLabelText('Name'), { target: { value: 'Steve Jobs' }})

  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(1)
})

在最后一个测试中,我们也在隐式测试handleNameChange,因为我们正在更改名称:) 所以name 不会为空并且registerUser 将被调用。

withStylestypescript 的示例在此 repo 中。
演示是here

【讨论】:

    【解决方案3】:

    您可以使用 unwrap 来解开包装样式的组件,然后对其进行测试

    import { unwrap } from '@material-ui/core/test-utils';
    import {ExampleForm, styles} from './Form';
    
    it('alerts on submit click', async () => {
      ...
    const unwrapped = unwrap(ExampleForm);
        ...
    }
    

    然后你可以对展开的对象进行所需的测试

    【讨论】:

      猜你喜欢
      • 2020-09-29
      • 2022-11-04
      • 1970-01-01
      • 2019-06-14
      • 2021-04-16
      • 1970-01-01
      • 2019-06-14
      • 2019-08-29
      • 2021-10-21
      相关资源
      最近更新 更多