如何使用 jest.mock 和 React 测试库来模拟 useRef

Jam*_*mes 5 unit-testing reactjs jestjs react-testing-library react-hooks

我有一个测试用例,我需要模拟 useRef 才能返回模拟当前值。我尝试了 jest.mock 但它返回一个 HTMLDivElement 。

代码:

   const ref = useRef<HTMLDivElement | null>(null);
Run Code Online (Sandbox Code Playgroud)

测试:

  jest.mock('react', () => {
     const originReact = jest.requireActual('react');
       return {
          ...originReact,
          useRef: jest.fn(),
       };
  });



  React.useRef.mockReturnValue({ current: {offsetWith: 100} }); 
Run Code Online (Sandbox Code Playgroud)

模拟回报

[ { type: 'return', value: { current: [HTMLDivElement] } } ]
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 7

@jonrsharpe的建议是组件测试的一般原则,但你的问题是一个特例,涉及DOM的一些属性和方法,例如offsetWidthgetBoundingClientRect()方法。无法返回的运行环境jsdom和浏览器运行环境下的渲染引擎返回的结果导致方法offsetWidth返回的属性值getBoundingClientRect()始终为0

\n

所以这里我们需要mockref

\n

这里的mockref也很特别。除了用于jest.mock()部分模拟之外,该属性还将在组件安装后ref.current分配。react为了拦截这个操作,我们需要创建一个ref带有对象属性的mock对象current,使用Object.defineProperty()方法来定义这个属性的getterand ,并拦截属性赋值。setterref.current

\n

jest.spyOn方法采用 accessType 的可选第三个参数,该参数可以是“get”或“set”,当您想要分别监视 getter 或 setter 时,这被证明是有用的。

\n

例如

\n

index.tsx

\n
import React, { useEffect, useRef } from 'react';\n\nexport default function App() {\n  const ref = useRef<HTMLDivElement>(null);\n  useEffect(() => {\n    console.log(ref.current?.offsetWidth);\n  }, [ref]);\n  return <div ref={ref}>app</div>;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

index.test.tsx

\n
import { render } from '@testing-library/react';\nimport React, { useRef } from 'react';\nimport { mocked } from 'ts-jest/utils';\nimport App from './';\n\njest.mock('react', () => {\n  return {\n    ...jest.requireActual<typeof React>('react'),\n    useRef: jest.fn(),\n  };\n});\n\nconst useMockRef = mocked(useRef);\n\ndescribe('66332902', () => {\n  afterEach(() => {\n    jest.clearAllMocks();\n  });\n  afterAll(() => {\n    jest.resetAllMocks();\n  });\n  test('should mock ref and offsetWidth', () => {\n    const ref = { current: {} };\n    Object.defineProperty(ref, 'current', {\n      set(_current) {\n        if (_current) {\n          jest.spyOn(_current, 'offsetWidth', 'get').mockReturnValueOnce(100);\n        }\n        this._current = _current;\n      },\n      get() {\n        return this._current;\n      },\n    });\n    useMockRef.mockReturnValueOnce(ref);\n    render(<App />);\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试结果:(见日志

\n
 PASS  examples/66332902/index.test.tsx (9.518 s)\n  66332902\n    \xe2\x9c\x93 should mock ref and offsetWidth (39 ms)\n\n  console.log\n    100\n\n      at examples/66332902/index.tsx:6:13\n\nTest Suites: 1 passed, 1 total\nTests:       1 passed, 1 total\nSnapshots:   0 total\nTime:        10.106 s\n
Run Code Online (Sandbox Code Playgroud)\n