如何使用 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-library
和 Jest
编写基本测试。请指导。
【问题讨论】:
你能发布这个问题的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
将被调用。
withStyles 和 typescript 的示例在此 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 提交?