如何使用 react-testing-library 测试包装在 withStyles 中的样式化 Material-UI 组件?

Posted

技术标签:

【中文标题】如何使用 react-testing-library 测试包装在 withStyles 中的样式化 Material-UI 组件?【英文标题】:How to test styled Material-UI components wrapped in withStyles using react-testing-library? 【发布时间】: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演示吗? 【参考方案1】:

为什么不将enzyme 与Full 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-library 的render 方法时,您不必担心使用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 是否正常工作。

    如果姓名为空,则不要致电api.registerUser
it('should not call api registerUser method', () => 
  const  getByTestId  = render(<Register />)
  fireEvent.click(getByTestId('button'))
  expect(api.registerUser).toHaveBeenCalledTimes(0)
)
    如果姓名为空,请致电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);
    ...

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

【讨论】:

以上是关于如何使用 react-testing-library 测试包装在 withStyles 中的样式化 Material-UI 组件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等

如何使用 Math.Net 连接矩阵。如何使用 Math.Net 调用特定的行或列?

WSARecv 如何使用 lpOverlapped?如何手动发出事件信号?