在 React 测试库中测试点击事件

时间:2021-02-04 09:50:24

标签: reactjs 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 会被调用一次并且 a <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 切换了)。你会使用什么断言来让这个测试通过这种情况?

非常感谢

1 个答案:

答案 0 :(得分:4)

你的方法有几个问题。

首先,创建这样的 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()
})