【问题标题】:Dispatch a Custom Event and test if it was correctly triggered (React TypeScript, Jest)调度自定义事件并测试它是否被正确触发(React TypeScript,Jest)
【发布时间】:2022-01-20 19:32:42
【问题描述】:

我正在尝试验证位于反应函数 useEffect 挂钩内的自定义事件侦听器,如下所示:


export interface specialEvent extends Event {
    detail?: string
}

function Example() {
    React.useEffect(()=>{
         document.addEventListener('specialEvent', handleChange)
         return () => {
               document.removeEventListener('specialEvent',handleChange)
          }
    })
    const handleChange = (event:SpecialEvent) => {
       ...
      }

}

我想触发这个自定义事件监听器并开玩笑地测试它:

it('should trigger "specialEvent" event Listener Properly', async () => {
        const specialEvent = new CustomEvent('specialEvent')
        const handleChange = jest.fn()
        render(<Example />)
        await waitFor(() => {
            window.document.dispatchEvent(specialEvent)
            expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
            expect(specialEvent).toHaveBeenCalledTimes(1)
        })
    })

这段代码给了我以下错误:

expect(received).toHaveBeenNthCalledWith(n, ...expected)

    Matcher error: received value must be a mock or spy function

    Received has type:  function
    Received has value: [Function dispatchEvent]

按照其中一个答案的建议,我尝试了这个:

//Assert Statements

const specialEvent = new CustomEvent('specialEvent');
const handleSelect = jest.fn();
act(() => { 
  render(<Example />) 
});
await waitFor(() => { 
  window.document.dispatchEvent(specialEvent) 
  expect(handleSelect).toHaveBeenCalledTimes(1) 
});

但这一次它说预期的呼叫是 1 但收到的是 0。

谁能帮我解决这个问题?

【问题讨论】:

    标签: reactjs typescript jestjs react-testing-library ts-jest


    【解决方案1】:

    在测试时,导致 React 状态更新的代码应该包装到 act(...) 中。如果 handleChange 不会导致 React 状态更新,则不需要使用 act

    此外,最好不要测试实现细节,对于您的情况,测试实现细节语句是:

    expect(window.document.dispatchEvent).toHaveBeenNthCalledWith(1, 'specialEvent')
    expect(specialEvent).toHaveBeenCalledTimes(1)
    

    对实现细节的每一个小改动都会导致需要修改测试用例。我们应该从用户的角度来测试 UI,用户并不关心 UI 的实现细节,只关心 UI 的正确渲染。

    您应该测试的是:当触发自定义事件并且在事件处理程序中更改状态时,组件的输出会发生什么。

    例如

    index.tsx:

    import React, { useState } from 'react';
    
    export interface SpecialEvent extends Event {
      detail?: string;
    }
    
    export function Example() {
      const [changed, setChanged] = useState(false);
      React.useEffect(() => {
        document.addEventListener('specialEvent', handleChange);
        return () => {
          document.removeEventListener('specialEvent', handleChange);
        };
      });
      const handleChange = (event: SpecialEvent) => {
        console.log(event);
        setChanged((pre) => !pre);
      };
      return <div>{changed ? 'a' : 'b'}</div>;
    }
    

    index.test.tsx:

    import { render, screen, act } from '@testing-library/react';
    import '@testing-library/jest-dom/extend-expect';
    import React from 'react';
    import { Example } from './';
    
    describe('70400540', () => {
      test('should pass', () => {
        const specialEvent = new CustomEvent('specialEvent');
        render(<Example />);
        expect(screen.getByText('b')).toBeInTheDocument();
        act(() => {
          window.document.dispatchEvent(specialEvent);
        });
        expect(screen.getByText('a')).toBeInTheDocument();
      });
    });
    

    window.document.dispatchEvent(specialEvent) 会导致 React 状态发生变化,所以我们将其包装成 act(...)

    测试结果:

     PASS  examples/70400540/index.test.tsx (11.259 s)
      70400540
        ✓ should pass (59 ms)
    
      console.log
        CustomEvent { isTrusted: [Getter] }
    
          at Document.handleChange (examples/70400540/index.tsx:16:13)
    
    Test Suites: 1 passed, 1 total
    Tests:       1 passed, 1 total
    Snapshots:   0 total
    Time:        12.655 s
    

    软件包版本:

    "@testing-library/react": "^11.2.2",
    "react": "^16.14.0",
    "jest": "^26.6.3",
    

    【讨论】:

      【解决方案2】:

      正如错误消息所述,toHaveBeenNthCalledWith 匹配器需要将mock 或间谍传递给expect

      但是,您可能不需要对 window.document.dispatchEvent 被调用做出任何断言,因为您知道您在测试中的上述行中调用了它。

      有关更多信息,请在此处查看toHaveBeenNthCalledWith 上的文档:https://jestjs.io/docs/expect#tohavebeennthcalledwithnthcall-arg1-arg2-

      【讨论】:

      • 我将代码更新为:expect(specialEvent).toHaveBeenCalledTimes(1),但它显示预期:1 调用:0。你知道可能出了什么问题:``` const specialEvent = new CustomEvent('specialEvent') const handleSelect = jest.fn() act(() => { render() }) await waitFor(() => { window.document.dispatchEvent(specialEvent) expect(handleSelect).toHaveBeenCalledTimes(1) }) ```
      猜你喜欢
      • 2017-12-15
      • 2021-03-23
      • 1970-01-01
      • 2021-06-14
      • 2016-07-05
      • 2022-06-11
      • 2017-12-11
      • 2015-03-26
      • 2020-05-22
      相关资源
      最近更新 更多