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

Posted

技术标签:

【中文标题】在 React 测试库中测试点击事件【英文标题】:Testing click event in React Testing Library 【发布时间】:2021-05-08 14:35:44 【问题描述】:

这是一个简单的子组件,当单击按钮时,它会向问题显示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 会被调用一次,并且屏幕上会出现&lt;p&gt; 元素:

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】:

您的方法存在一些问题。

首先,像这样创建 onClick 模拟不会模拟按钮的 onClick 回调。回调是组件内部的,您无法从测试中访问它。你可以做的是测试触发onClick 事件的结果,在这种情况下,这意味着验证&lt;FiMinusCircle /&gt; 被呈现而不是&lt;FiPlusCircle /&gt;

其次,p 不是有效角色 - 如果 RTL 找不到您搜索的角色,则 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()
)

【讨论】:

感谢 Julio,我已经编辑了这个问题,因为我突然意识到我只是对角色一无所知。 onClick 东西现在也很有意义。你能看一下编辑吗,我在这个问题中添加了另一部分(从中删除了一些原始的愚蠢)-我正在寻找一种可靠的方法来测试在屏幕上和屏幕外切换的 DOM 元素。干杯 @Adam 当然,请参阅我的更新答案。同样的逻辑也适用。【参考方案2】:

在我的情况下,这有效:

it('Does click event', () => 
    const  container  = render(<Component />);

    fireEvent.click(container.querySelector('.your-btn-classname'));

    // click evt was triggered
);

【讨论】:

以上是关于在 React 测试库中测试点击事件的主要内容,如果未能解决你的问题,请参考以下文章

在苹果 iphone 上,点击通知会捕获啥事件?

Angular中的单元测试点击事件

react事件代理

事件点击单元测试VUE

使用 vue-test-utils 测试两个不同的点击事件

深入React事件系统(React点击空白部分隐藏弹出层;React阻止事件冒泡失效)