在 React 测试库中测试点击事件
Testing click event in React Testing Library
这是一个简单的子组件,它在单击按钮时向问题显示 answer
:
const Question = ({ question, answer }) => {
const [showAnswer, setShowAnswer] = useState(false)
return (
<>
<article>
<header>
<h2 data-testid="question">{question}</h2>
<button onClick={() => setShowAnswer(!showAnswer)}>
{
!showAnswer ? <FiPlusCircle /> : <FiMinusCircle />
}
</button>
</header>
{
showAnswer && <p data-testid="answer">{answer}</p>
}
</article>
</>
)
}
export default Question;
我正在尝试测试当单击 button
时,附加的 onClick
被调用一次并且 <p>
元素出现在屏幕上:
const onClick = jest.fn()
test('clicking the button toggles an answer on/off', () => {
render(<Question />);
const button = screen.getByRole('button')
fireEvent.click(button)
expect(onClick).toHaveBeenCalledTimes(1);
expect(screen.getByTestId('answer')).toBeInTheDocument()
fireEvent.click(button)
expect(screen.getByTestId('answer')).not.toBeInTheDocument()
screen.debug()
})
RTL 表示 onClick
根本没有被调用(在 UI 中被调用,因为结果符合预期)
此外,如果我想测试此按钮是否真的会切换 answer
元素(消息应该打开和关闭),我将如何测试它?
如果我将另一个 fireEvent.click()
添加到测试中(模拟第二次单击应触发 answer
元素关闭的按钮),然后添加
expect(screen.getByTestId('answer')).not.toBeInTheDocument()
RTL 将不会找到该元素(这很好,我想,这意味着它已经真正关闭了 DOM)。你会使用什么断言来让这个测试通过那个案例?
你的方法有几个问题。
首先,创建一个像这样的 onClick
模拟不会模拟按钮的 onClick
回调。回调是组件内部的,您无权从测试中访问它。您可以做的是测试触发 onClick
事件的结果,在这种情况下,这意味着验证呈现的是 <FiMinusCircle />
而不是 <FiPlusCircle />
.
其次,p
不是有效角色 - 如果找不到您搜索的角色,RTL 会告诉您 DOM 中哪些角色可用。 paragraph
元素没有固有的可访问性角色,因此您最好通过 getByText
的内容来访问它。
这是测试的更新版本:
test('clicking the button toggles an answer on/off', () => {
render(<Question question="Is RTL great?" answer="Yes, it is." />);
const button = screen.getByRole('button')
fireEvent.click(button)
// Here you'd want to test if `<FiMinusCircle />` is rendered.
expect(/* something from FiMinusCircle */).toBeInTheDocument()
expect(screen.getByText('Yes, it is.')).toBeInTheDocument()
fireEvent.click(button)
// Here you'd want to test if `<FiPlusCircle />` is rendered.
expect(/* something from FiPlusCircle */).toBeInTheDocument();
expect(screen.queryByText('Yes, it is.')).not.toBeInTheDocument()
})
在我的例子中这有效:
it('Does click event', () => {
const { container } = render(<Component />);
fireEvent.click(container.querySelector('.your-btn-classname'));
// click evt was triggered
});
这是一个简单的子组件,它在单击按钮时向问题显示 answer
:
const Question = ({ question, answer }) => {
const [showAnswer, setShowAnswer] = useState(false)
return (
<>
<article>
<header>
<h2 data-testid="question">{question}</h2>
<button onClick={() => setShowAnswer(!showAnswer)}>
{
!showAnswer ? <FiPlusCircle /> : <FiMinusCircle />
}
</button>
</header>
{
showAnswer && <p data-testid="answer">{answer}</p>
}
</article>
</>
)
}
export default Question;
我正在尝试测试当单击 button
时,附加的 onClick
被调用一次并且 <p>
元素出现在屏幕上:
const onClick = jest.fn()
test('clicking the button toggles an answer on/off', () => {
render(<Question />);
const button = screen.getByRole('button')
fireEvent.click(button)
expect(onClick).toHaveBeenCalledTimes(1);
expect(screen.getByTestId('answer')).toBeInTheDocument()
fireEvent.click(button)
expect(screen.getByTestId('answer')).not.toBeInTheDocument()
screen.debug()
})
RTL 表示 onClick
根本没有被调用(在 UI 中被调用,因为结果符合预期)
此外,如果我想测试此按钮是否真的会切换 answer
元素(消息应该打开和关闭),我将如何测试它?
如果我将另一个 fireEvent.click()
添加到测试中(模拟第二次单击应触发 answer
元素关闭的按钮),然后添加
expect(screen.getByTestId('answer')).not.toBeInTheDocument()
RTL 将不会找到该元素(这很好,我想,这意味着它已经真正关闭了 DOM)。你会使用什么断言来让这个测试通过那个案例?
你的方法有几个问题。
首先,创建一个像这样的 onClick
模拟不会模拟按钮的 onClick
回调。回调是组件内部的,您无权从测试中访问它。您可以做的是测试触发 onClick
事件的结果,在这种情况下,这意味着验证呈现的是 <FiMinusCircle />
而不是 <FiPlusCircle />
.
其次,p
不是有效角色 - 如果找不到您搜索的角色,RTL 会告诉您 DOM 中哪些角色可用。 paragraph
元素没有固有的可访问性角色,因此您最好通过 getByText
的内容来访问它。
这是测试的更新版本:
test('clicking the button toggles an answer on/off', () => {
render(<Question question="Is RTL great?" answer="Yes, it is." />);
const button = screen.getByRole('button')
fireEvent.click(button)
// Here you'd want to test if `<FiMinusCircle />` is rendered.
expect(/* something from FiMinusCircle */).toBeInTheDocument()
expect(screen.getByText('Yes, it is.')).toBeInTheDocument()
fireEvent.click(button)
// Here you'd want to test if `<FiPlusCircle />` is rendered.
expect(/* something from FiPlusCircle */).toBeInTheDocument();
expect(screen.queryByText('Yes, it is.')).not.toBeInTheDocument()
})
在我的例子中这有效:
it('Does click event', () => {
const { container } = render(<Component />);
fireEvent.click(container.querySelector('.your-btn-classname'));
// click evt was triggered
});