在React测试库中检查按钮是否已禁用。

123

我有一个 React 组件,生成一个包含 <span> 元素的按钮,类似于这个:

function Click(props) {
    return (
        <button disable={props.disable}>
            <span>Click me</span>
        </button>
    );
}

我想用react-testing-librarymocha+ chai来测试这个组件的逻辑。

目前我卡住的问题是,getByText("Click me")选择器返回的是 DOM节点,但是为了测试,我需要检查


测试按钮元素的禁用属性是否为真? - jonrsharpe
1
不是 OP 的情况,但是对于未来来到这里想要测试 aria-disabled 的访问者,.toBeDisabled() 不起作用。解决方法是使用 .toHaveAttribute('aria-disabled', 'true')。请参见 jest-dom #144 - ggorlen
10个回答

208

如果按钮被禁用,请进行断言

您可以使用toHaveAttributeclosest进行测试。

import { render } from '@testing-library/react';

const { getByText } = render(Click);
expect(getByText(/Click me/i).closest('button')).toHaveAttribute('disabled');

或者 toBeDisabled

expect(getByText(/Click me/i).closest('button')).toBeDisabled();

检查按钮是否已启用

要检查按钮是否已启用,请使用如下方式:not

expect(getByText(/Click me/i).closest('button')).not.toBeDisabled();

3
只是一条注释:在你的示例中使用最新版本的jsdom库才能使用closest(...)函数(v11.12.0或更高版本)。 - Nikita Sivukhin
13
请注意,toHaveAttribute需要安装https://github.com/testing-library/jest-dom。 - Sam
3
对于使用不同于jest的框架(例如Jasmine)的所有人,可以使用类似以下代码的方式来完成任务:expect(getByText(/Click me/i).closest('button').hasAttribute('disabled')).toBeTrue(); - René Schubert
虽然这个答案可行,但我个人觉得它不符合react-testing-library的理念。我在下面提供了另一种答案,对我来说更符合rtl(并不是说更好或更差,只是另一种选择)。 - Chris
1
我对这种专注于实现细节的方法与其他人有类似的感觉,但值得注意的是,在testing-library自己的jest-dom实现中,.toBeDisabled()只是检查属性,并且他们在自己的指南中推荐使用此匹配器来检查禁用状态。 - Shawn Erquhart
显示剩余5条评论

39
你可以使用@testing-library/jest-dom中的toBeDisabled(),它是一个自定义的Jest匹配器,用于测试DOM状态。 https://github.com/testing-library/jest-dom 示例:
<button>Submit</button>
expect(getByText(/submit/i)).toBeDisabled()

18

对于想找到按钮未被禁用的测试的人。

import { render } from '@testing-library/react';

const { getByText } = render(Click);
expect(getByText(/Click me/i).getAttribute("disabled")).toBe(null)

3
使用.toBeNull()代替.toBe(null) - Sebastian Nielsen

14

我认为你正在测试一项实现细节,而这是 react-testing-library 不鼓励的。

你的测试越像你的软件使用方式,你得到的信心就越多。

如果一个按钮被禁用,用户看不到禁用的属性,相反他们什么也看不见。如果一个按钮被启用,用户看不到禁用属性的省略,相反他们会看到某些事情发生了。

我认为你应该测试此功能:

const Button = (props) => (
  <button 
    type="submit" 
    onClick={props.onClick} 
    disabled={props.disabled}
  >
    Click me
  </button>
);

describe('Button', () => {
  it('will call onClick when enabled', () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick} disabled={false} />);
    userEvent.click(getByRole('button', /click me/i));
    expect(onClick).toHaveBeenCalledTimes(1);
  });

  it('will not call onClick when disabled', () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick} disabled={true} />);
    userEvent.click(getByRole('button', /click me/i));
    expect(onClick).not.toHaveBeenCalled();
  });
})

3
好的!虽然我认为在禁用按钮的情况下,您可以牺牲概念的纯度并测试“实现细节”,特别是当您测试具有固定行为的HTML规范的一部分的 disabled 属性时。您能否提供一些例子,以展示测试 disabled 属性的缺点和模拟 click 回调的优点,只是出于我的兴趣? - Nikita Sivukhin
正如@NikitaSivukhin所述,当您检查单击禁用按钮是否会触发OnClick时,您正在测试HTML固定行为。虽然用户无法看到属性是否已禁用,但他们将看到禁用属性的视觉效果。 - Logan Cundiff
当然。基本上是“永远不要说永远”——总会有例外的“规则”。我个人不会检查禁用属性,但可能有情况下牺牲概念的纯度更有意义,就像Nikita所说的那样。最终,我不了解你的代码库,你应该根据团队和项目的需要来做。 - Chris
1
@Chris Yea,我认为你提出这个观点很好。在考虑如何实施测试时,值得问这些问题,而不是采取一种适合所有情况的方法。好观点。 - Logan Cundiff

6

toHaveAttribute 是在使用属性时的一个不错选择。

<button data-testid="ok-button" type="submit" disabled>ok</button>

const button = getByTestId('ok-button')
//const button = getByRole('button');

expect(button).toHaveAttribute('disabled')
expect(button).toHaveAttribute('type', 'submit')
expect(button).not.toHaveAttribute('type', 'button')

expect(button).toHaveAttribute('type', expect.stringContaining('sub'))
expect(button).toHaveAttribute('type', expect.not.stringContaining('but'))

希望这个能有所帮助。

3
可以使用@testing-library/react来测试按钮的disable属性,示例如下:
   import { render } from '@testing-library/react';

   const {getByText} = render(<Click/>)

   expect(getByText('Click me').closest('button').disabled).toBeTruthy()

我喜欢这个答案,因为它不依赖于其他库。谢谢。 - Rokit

1

我的解决方案是,这个案例似乎已经涵盖了必要的内容。请检查按钮是否被禁用,因此toHaveBeenCalledTimes必须接收0。

 test('Will not call onClick when disabled', () => {
        const mockHandler = jest.fn()

        render(<Button title="Disabled account" disabled={true} onClick={mockHandler} />)
        const button = screen.getByText("Disabled account")
        fireEvent.click(button)

        expect(mockHandler).toHaveBeenCalledTimes(0)
        expect(button).toHaveProperty('disabled', true)
    })

1
另一种解决方法是通过角色抓取并检查innerHTML,例如:
const { getByRole } = render(<Click />)
const button = getByRole('button')

// will make sure the 'Click me' text is in there somewhere
expect(button.innerHTML).toMatch(/Click me/))


这并不是针对你的具体情况最佳解决方案,但如果你需要处理一个不是实际按钮的按钮组件,比如 <div role="button"><span>Click Me</span></div>,那么这是一个备选方案。

0

当前答案:

expect(await screen.findByRole('button', { name: 'Click me' })).toBeEnabled();

0
你可以使用 toBeDisabled()
expect(getByText(/Click me/i).closest('button')).toBeDisabled();

检查按钮是否已启用。
要检查按钮是否已启用,请使用以下方式:
expect(getByText(/Click me/i).closest('button')).not.toBeDisabled();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接