如何测试 Material-UI 复选框是否被 react-testing-library 选中?
How to test Material-UI Checkbox is checked with react-testing-library?
似乎 Material UI 的工作方式是在单击复选框时呈现不同的 SVG,而不更改实际输入元素上的属性或任何内容。那么我如何实际测试该元素是否符合 react-testing-library
理念?
这是
的粗略示例
复选框组件使用
export const CheckBoxContainer = () => (
<Checkbox inputProps={{ 'data-testid': `clickable-checkbox-1234` }} data-testid={`checkbox-1234`} />
);
测试
test('check the box', async () => {
const { getByTestId } = render(<CheckBoxContainer />);
await waitForElement(() => getByTestId(`checkbox-1234`));
const checkbox = getByTestId(`checkbox-1234`);
fireEvent.click(getByTestId(`clickable-checkbox-1234`));
expect(checkbox).toHaveAttribute('checked');
});
由 Material UI
生成 HTML
<span
class="MuiButtonBase-root-54 MuiIconButton-root-48 MuiSwitchBase-root-231 MuiCheckbox-root-225 MuiCheckbox-colorSecondary-230 MuiSwitchBase-checked-232 MuiCheckbox-checked-226"
data-testid="checkbox-1234"
>
<span class="MuiIconButton-label-53">
<svg
class="MuiSvgIcon-root-57"
focusable="false"
viewBox="0 0 24 24"
aria-hidden="true"
role="presentation"
>
<path
d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
></path>
</svg>
<input
class="MuiSwitchBase-input-234"
type="checkbox"
data-indeterminate="false"
data-testid="clickable-checkbox-1234"
value=""
/>
</span>
<span class="MuiTouchRipple-root-66"> </span>
</span>
由于复选框正在渲染 input
我会使用它而不是专注于图像。
你可以这样做:
const checkbox = getByTestId('checkbox-1234').querySelector('input[type="checkbox"]')
expect(checkbox).toHaveProperty('checked', true)
想通了。需要在DOM上使用checked
getter,而不是尝试检查checked
属性。发现这个问题解释它:https://github.com/facebook/react/issues/7051#issuecomment-334018747
这个 Codepen 演示了 checked 属性如何在 React 中保持不变:
https://codepen.io/gaearon/pen/wgedvV?editors=0010
像这样:
expect(checkbox.checked).toBe(true)
在 OnChange 函数中,您可以访问 事件对象 和 checked 值。
OnChange(event:object, checked:boolean)
这是文档中写的。
另一种方法是从事件中访问 checked 属性,如下所示:
event.target.checked
这在文档中也有规定。
似乎在 JSDOM 中选中的 属性 没有更新,例如
fireEvent.click(input)
console.log(input.checked) //false
fireEvent.click(input)
console.log(input.checked) //false
这使得很难确定复选框的当前状态。由于 fireEvent.click(input)
本质上是一个切换,如果您有一个测试需要将值设置为 true(打勾),而不管复选框的初始状态如何,它就会变得棘手。您可能有一个更新表单,其中可以根据许多不同的服务器数据装置检查/不检查复选框的初始状态。幸运的是,有了 MUI,我们可以通过检测周围的一些 DOM 类 来确定状态。所以我做了以下辅助函数...
const setMuiCheckBox = (input: HTMLInputElement, newValue: boolean) => {
// find the root span of our checkbox
const root = input.closest('.MuiButtonBase-root')
if (root) {
const isChecked = root.classList.contains('Mui-checked')
if (isChecked !== newValue) {
fireEvent.click(input)
}
}
}
我发现定位复选框的最简单方法是为其添加标签
<FormControlLabel
htmlFor="first-checkBox"
label="First Checkbox"
control={ <Checkbox
checked={checked}
onChange={handleChange}
inputProps={{ 'aria-label': 'primary checkbox' }}
/>
}
/>
稍后在测试用例中
const checkboxEl = screen.getByLabelText('First Checkbox') as HTMLInputElement
expect(checkboxEl).toBeChecked()
如果测试失败是因为无法将 toBeChecked 识别为可用断言的一部分,那是因为您需要导入
导入'@testing-library/jest-dom/extend-expect'
试试这个,
import React from 'react';
import {useState, useEffect} from 'react';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
const CheckBox = () => {
const [rememberMe, setRememberMe] = useState(false);
const handleRememberMeChange = () => {
if (rememberMe === true || rememberMe === false){
setRememberMe(!rememberMe);
}
else{
setRememberMe(true);
}
};
return (
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
onChange = {handleRememberMeChange}
/>
);
}
export default CheckBox;
复选框的值作为布尔值存储在状态中。
更新:
显然,您可以在处理程序中使用 event.target.checked
获取复选框的值。所以上面的处理程序可以改为,
const handleRememberMeChange = (event) => {
setRememberMe(event.target.checked);
};
我的复选框组件受到控制,单击处理程序更改了父组件的状态,然后将其传递给复选框。所以我使用 mockImplementation 重新渲染具有所需 checked
值
的组件
test('calls handleSelect with correct argument on click', () => {
const mockSelectOne = jest.fn();
const { getByRole, rerender } = render(<Checkbox onClick={() => mockSelectOne('id')} />);
expect(getByRole('checkbox')).not.toBeChecked();
mockSelectOne.mockImplementation(() => rerender(<Checkbox onClick={() => mockSelectOne('id')} checked />));
userEvent.click(getByRole('checkbox'));
expect(mockSelectOne).toBeCalledTimes(1);
expect(mockSelectOne).toBeCalledWith('id');
expect(getByRole('checkbox')).toBeChecked();
});
似乎 Material UI 的工作方式是在单击复选框时呈现不同的 SVG,而不更改实际输入元素上的属性或任何内容。那么我如何实际测试该元素是否符合 react-testing-library
理念?
这是
的粗略示例复选框组件使用
export const CheckBoxContainer = () => (
<Checkbox inputProps={{ 'data-testid': `clickable-checkbox-1234` }} data-testid={`checkbox-1234`} />
);
测试
test('check the box', async () => {
const { getByTestId } = render(<CheckBoxContainer />);
await waitForElement(() => getByTestId(`checkbox-1234`));
const checkbox = getByTestId(`checkbox-1234`);
fireEvent.click(getByTestId(`clickable-checkbox-1234`));
expect(checkbox).toHaveAttribute('checked');
});
由 Material UI
生成 HTML<span
class="MuiButtonBase-root-54 MuiIconButton-root-48 MuiSwitchBase-root-231 MuiCheckbox-root-225 MuiCheckbox-colorSecondary-230 MuiSwitchBase-checked-232 MuiCheckbox-checked-226"
data-testid="checkbox-1234"
>
<span class="MuiIconButton-label-53">
<svg
class="MuiSvgIcon-root-57"
focusable="false"
viewBox="0 0 24 24"
aria-hidden="true"
role="presentation"
>
<path
d="M19 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.11 0 2-.9 2-2V5c0-1.1-.89-2-2-2zm-9 14l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
></path>
</svg>
<input
class="MuiSwitchBase-input-234"
type="checkbox"
data-indeterminate="false"
data-testid="clickable-checkbox-1234"
value=""
/>
</span>
<span class="MuiTouchRipple-root-66"> </span>
</span>
由于复选框正在渲染 input
我会使用它而不是专注于图像。
你可以这样做:
const checkbox = getByTestId('checkbox-1234').querySelector('input[type="checkbox"]')
expect(checkbox).toHaveProperty('checked', true)
想通了。需要在DOM上使用checked
getter,而不是尝试检查checked
属性。发现这个问题解释它:https://github.com/facebook/react/issues/7051#issuecomment-334018747
这个 Codepen 演示了 checked 属性如何在 React 中保持不变: https://codepen.io/gaearon/pen/wgedvV?editors=0010
像这样:
expect(checkbox.checked).toBe(true)
在 OnChange 函数中,您可以访问 事件对象 和 checked 值。
OnChange(event:object, checked:boolean)
这是文档中写的。
另一种方法是从事件中访问 checked 属性,如下所示:
event.target.checked
这在文档中也有规定。
似乎在 JSDOM 中选中的 属性 没有更新,例如
fireEvent.click(input)
console.log(input.checked) //false
fireEvent.click(input)
console.log(input.checked) //false
这使得很难确定复选框的当前状态。由于 fireEvent.click(input)
本质上是一个切换,如果您有一个测试需要将值设置为 true(打勾),而不管复选框的初始状态如何,它就会变得棘手。您可能有一个更新表单,其中可以根据许多不同的服务器数据装置检查/不检查复选框的初始状态。幸运的是,有了 MUI,我们可以通过检测周围的一些 DOM 类 来确定状态。所以我做了以下辅助函数...
const setMuiCheckBox = (input: HTMLInputElement, newValue: boolean) => {
// find the root span of our checkbox
const root = input.closest('.MuiButtonBase-root')
if (root) {
const isChecked = root.classList.contains('Mui-checked')
if (isChecked !== newValue) {
fireEvent.click(input)
}
}
}
我发现定位复选框的最简单方法是为其添加标签
<FormControlLabel
htmlFor="first-checkBox"
label="First Checkbox"
control={ <Checkbox
checked={checked}
onChange={handleChange}
inputProps={{ 'aria-label': 'primary checkbox' }}
/>
}
/>
稍后在测试用例中
const checkboxEl = screen.getByLabelText('First Checkbox') as HTMLInputElement
expect(checkboxEl).toBeChecked()
如果测试失败是因为无法将 toBeChecked 识别为可用断言的一部分,那是因为您需要导入
导入'@testing-library/jest-dom/extend-expect'
试试这个,
import React from 'react';
import {useState, useEffect} from 'react';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Checkbox from '@material-ui/core/Checkbox';
const CheckBox = () => {
const [rememberMe, setRememberMe] = useState(false);
const handleRememberMeChange = () => {
if (rememberMe === true || rememberMe === false){
setRememberMe(!rememberMe);
}
else{
setRememberMe(true);
}
};
return (
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
onChange = {handleRememberMeChange}
/>
);
}
export default CheckBox;
复选框的值作为布尔值存储在状态中。
更新:
显然,您可以在处理程序中使用 event.target.checked
获取复选框的值。所以上面的处理程序可以改为,
const handleRememberMeChange = (event) => {
setRememberMe(event.target.checked);
};
我的复选框组件受到控制,单击处理程序更改了父组件的状态,然后将其传递给复选框。所以我使用 mockImplementation 重新渲染具有所需 checked
值
test('calls handleSelect with correct argument on click', () => {
const mockSelectOne = jest.fn();
const { getByRole, rerender } = render(<Checkbox onClick={() => mockSelectOne('id')} />);
expect(getByRole('checkbox')).not.toBeChecked();
mockSelectOne.mockImplementation(() => rerender(<Checkbox onClick={() => mockSelectOne('id')} checked />));
userEvent.click(getByRole('checkbox'));
expect(mockSelectOne).toBeCalledTimes(1);
expect(mockSelectOne).toBeCalledWith('id');
expect(getByRole('checkbox')).toBeChecked();
});