【问题标题】:How to test Material-UI Checkbox is checked with react-testing-library?如何使用 react-testing-library 检查 Material-UI Checkbox?
【发布时间】:2018-11-12 23:36:15
【问题描述】:

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>

【问题讨论】:

    标签: material-ui react-testing-library


    【解决方案1】:

    由于复选框正在渲染input,我会使用它而不是专注于图像。

    你可以这样做:

    const checkbox = getByTestId('checkbox-1234').querySelector('input[type="checkbox"]')
    expect(checkbox).toHaveProperty('checked', true)
    

    【讨论】:

    • 输入没有改变,也永远不会得到“checked”属性,SVG才是真正改变的地方
    • 这很奇怪。你能在codeandbox中创建一个可重现的例子吗?
    • 从 Dan Abramov 找到一个例子:codepen.io/gaearon/pen/wgedvV?editors=0010
    • 找到了一种在不冒犯testing-library/no-node-access rule的情况下编写此内容的方法:const checkbox = within(getByTestId('checkbox-1234')).getByRole('checkbox')
    【解决方案2】:

    我发现找到复选框的最简单方法是为其添加标签

    <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'

    【讨论】:

      【解决方案3】:

      想通了。需要在 DOM 上使用 checked getter,而不是尝试检查 checked 属性。发现这个问题解释它:https://github.com/facebook/react/issues/7051#issuecomment-334018747

      这个 Codepen 演示了在 React 中选中的属性是如何不改变的: https://codepen.io/gaearon/pen/wgedvV?editors=0010

      像这样: expect(checkbox.checked).toBe(true)

      【讨论】:

      • 我不明白这与我的版本有何不同。 expect(checkbox).toHaveProperty('checked', true)expect(checkbox.checked).toBe(true) 的语法糖
      【解决方案4】:

      似乎在 JSDOM 中选中的属性没有得到更新,例如

            fireEvent.click(input)
            console.log(input.checked) //false
            fireEvent.click(input)
            console.log(input.checked) //false
      

      这使得确定复选框的当前状态变得困难。由于fireEvent.click(input) 本质上是一个切换,如果您有一个需要将值设置为 true(tick)的测试,无论复选框的初始状态如何,它都会变得很棘手。您可能有一个更新表单,其中复选框的初始状态可以根据许多不同的服务器数据夹具进行检查/未检查。幸运的是,使用 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)
          }
        }
      }
      

      【讨论】:

        【解决方案5】:

        试试这个,

        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;
        

        Checkbox 的值作为布尔值存储在状态中。

        更新: 显然,您可以在处理程序中使用 event.target.checked 获取 Checkbox 的值。所以上面的处理程序可以改为,

            const handleRememberMeChange = (event) => {
                setRememberMe(event.target.checked);
            };
        

        【讨论】:

          【解决方案6】:

          我的复选框组件受到控制,并且单击处理程序会更改父组件的状态,然后将其传递给复选框。所以我使用了 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();
            });
          

          【讨论】:

            【解决方案7】:

            在 OnChange 函数中,您可以访问 事件对象checked 值。

            OnChange(event:object, checked:boolean)
            

            这是在文档中写的。

            另一种方法是从事件中访问 checked 属性,如下所示:

            event.target.checked
            

            这也是在文档中指定的。

            文档:https://material-ui.com/api/checkbox/

            【讨论】:

            • 无关答案
            猜你喜欢
            • 2021-02-09
            • 1970-01-01
            • 2021-08-09
            • 2021-02-10
            • 2021-04-16
            • 2022-11-04
            • 2020-03-15
            • 2019-08-06
            • 1970-01-01
            相关资源
            最近更新 更多