如何使用 jest.mock 模拟 useRef 和反应测试库
Posted
技术标签:
【中文标题】如何使用 jest.mock 模拟 useRef 和反应测试库【英文标题】:How to mock useRef using jest.mock and react testing library 【发布时间】:2021-05-25 17:03:28 【问题描述】:我有一个测试用例,我需要模拟 useRef 以返回模拟当前值。我试过 jest.mock 但它返回一个 htmlDivElement 。
代码:
const ref = useRef<HTMLDivElement | null>(null);
测试:
jest.mock('react', () =>
const originReact = jest.requireActual('react');
return
...originReact,
useRef: jest.fn(),
;
);
React.useRef.mockReturnValue( current: offsetWith: 100 );
模拟回报
[ type: 'return', value: current: [HTMLDivElement] ]
【问题讨论】:
不要模拟任何的 React API,你不拥有它。测试组件的行为,而不是实现。 我明白了。有没有办法测试当前参考值(如 offsetWidth)是否发生变化。谢谢 【参考方案1】:@jonrsharpe 的建议是组件测试的一般原则,但你的问题是一个特例,涉及到 DOM 的一些属性和方法,例如offsetWidth
和getBoundingClientRect()
方法。 jsdom
的运行环境无法返回,浏览器运行环境下的渲染引擎返回结果导致offsetWidth
和getBoundingClientRect()
方法返回的属性值始终为0
。
所以这里我们需要模拟ref
。
这里的模拟ref
也很特别。除了使用jest.mock()
进行部分模拟外,ref.current
属性将在组件挂载后由react
分配。为了拦截这个操作,我们需要创建一个带有current
对象属性的模拟ref
对象,使用Object.defineProperty()
方法为这个ref.current
属性定义getter
和setter
,并拦截属性任务。
jest.spyOn
方法采用 accessType 的第三个可选参数,可以是 'get' 或 'set',当您想分别监视 getter 或 setter 时,这被证明是有用的。
例如
index.tsx
:
import React, useEffect, useRef from 'react';
export default function App()
const ref = useRef<HTMLDivElement>(null);
useEffect(() =>
console.log(ref.current?.offsetWidth);
, [ref]);
return <div ref=ref>app</div>;
index.test.tsx
:
import render from '@testing-library/react';
import React, useRef from 'react';
import mocked from 'ts-jest/utils';
import App from './';
jest.mock('react', () =>
return
...jest.requireActual<typeof React>('react'),
useRef: jest.fn(),
;
);
const useMockRef = mocked(useRef);
describe('66332902', () =>
afterEach(() =>
jest.clearAllMocks();
);
afterAll(() =>
jest.resetAllMocks();
);
test('should mock ref and offsetWidth', () =>
const ref = current: ;
Object.defineProperty(ref, 'current',
set(_current)
if (_current)
jest.spyOn(_current, 'offsetWidth', 'get').mockReturnValueOnce(100);
this._current = _current;
,
get()
return this._current;
,
);
useMockRef.mockReturnValueOnce(ref);
render(<App />);
);
);
测试结果:(查看日志)
PASS examples/66332902/index.test.tsx (9.518 s)
66332902
✓ should mock ref and offsetWidth (39 ms)
console.log
100
at examples/66332902/index.tsx:6:13
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.106 s
【讨论】:
这不起作用,如果在<App>
内部其他组件调用useRef。知道如何解决这个@slideshowp2 吗?以上是关于如何使用 jest.mock 模拟 useRef 和反应测试库的主要内容,如果未能解决你的问题,请参考以下文章
jest.mock():如何使用工厂参数模拟 ES6 类默认导入
使用 Jest 模拟的服务导致“不允许 jest.mock() 的模块工厂引用任何超出范围的变量”错误