如何使用反应测试库模拟 ResizeObserver 以在单元测试中工作

Posted

技术标签:

【中文标题】如何使用反应测试库模拟 ResizeObserver 以在单元测试中工作【英文标题】:How to mock ResizeObserver to work in unit tests using react testing library 【发布时间】:2021-02-09 22:42:04 【问题描述】:

如果有人可以提供帮助,我有一个自定义钩子,它使用 ResizeObserver 来更改组件的宽度。我的问题是,当我去运行我的单元测试时,它会破坏我的所有测试并查看快照,它并没有渲染 dom 中的所有元素。在我实现 ResizeObserver 之前它一直在工作。有谁知道我是否有办法将 ResizeObserver 模拟为未定义。或其他建议。

import * as React from 'react';
import ResizeObserver from 'resize-observer-polyfill';

const useResizeObserver = (ref:  current: any ) => 
    const [dimensions, setDimensions] = React.useState<DOMRectReadOnly>();
    React.useEffect(() => 
        const observeTarget = ref.current;
        const resizeObserver = new ResizeObserver((entries) => 
            entries.forEach((entry) => 
                setDimensions(entry.contentRect);
            );
        );
        resizeObserver.observe(observeTarget);
        return () => 
            resizeObserver.unobserve(observeTarget);
        ;
    , [ref]);
    return dimensions;
;

export default useResizeObserver;



import  render, screen, waitFor  from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';

import mockFetchProfileActivity from '../../../services/mocks/fetch-profile-activity';
import BarChart from './BarChart';

const component = <BarChart userActivity=mockFetchProfileActivity />;

describe('Render barElement Chart component', () => 
    const observers: any[] = [];
    let resizeHandler: (observers: any[]) => void;
    (window as any).ResizeObserver = (e: any) => 
        resizeHandler = e;

        return 
            observe(element: any) 
                observers.push(element);
            ,
            unobserve(element: any) 
                const i = observers.indexOf(element);
                if (i !== -1) 
                    observers.splice(i, 1);
                
            
        ;
    ;

    it('Matches the snapshot', () => 
        // resizeHandler(observers);
        const container = render(component);
        expect(container).toMatchSnapshot();
    );

    it('when clicking on a chart barElement drilldown "challenges" are shown', async () => 
        // arrange
        const componentRender = render(component);
        waitFor(() => resizeHandler(observers));

        // act
        const barElement = componentRender.container.querySelector('svg rect');

        if (barElement) userEvent.click(barElement);

        // assert
        expect(screen.getByText('Challenge 1')).toBeInTheDocument();
    );
);

【问题讨论】:

【参考方案1】:

我选择将 polyfill 添加为开发依赖项,并将以下行添加到 setupTests.js/ts:

global.ResizeObserver = require('resize-observer-polyfill')

【讨论】:

非常流畅,并且完美运行。我喜欢!为了让您的类型很好地匹配,请将其换成 import 等效项,然后在下一行中分配它。 不错!我不得不这样做 window.ResizeObserver = require... 但后来它没有问题。【参考方案2】:

模拟 ResizeObserver:

class ResizeObserver 
    observe() 
        // do nothing
    
    unobserve() 
        // do nothing
    


window.ResizeObserver = ResizeObserver;
export default ResizeObserver;

sample.test.js

import ResizeObserver from './__mocks__/ResizeObserver';
import module from 'sample';

describe('module', ()=> 
     it('returns an instance of ResizeObserver', () => 
           // do something that uses the resize observer
           // NOTE: The actual observe handler would not be called in jsdom anyway as no resize would be triggered.
           // e.g.
           expect(module.somethingThatReturnAReference to the resize observer).toBeInstanceOf(ResizeObserver);
        );
);

source

【讨论】:

我如何测试 resizeObserver 回调中发生了什么?例如,我对需要测试的 html 元素样式进行了一些更改。怎么样?【参考方案3】:

我在使用 Create React App 设置时遇到了类似的问题。

如果是这种情况,您可以在根目录中创建一个名为 setupTest.js 的文件并添加以下代码:

  import '@testing-library/jest-dom/extend-expect';
  import 'jest-extended';
    
  jest.mock('./hooks/useResizeObserver', () => () => (
    __esModule: true,
    default: jest.fn().mockImplementation(() => (
        observe: jest.fn(),
        unobserve: jest.fn(),
        disconnect: jest.fn(),
    )),
  ));

您可以找到更多信息来配置 Create React App here 和 ResizeObserver API here 的测试环境

【讨论】:

看起来不错,唯一的问题是我得到一个错误,你真的得到这个工作了吗这是我得到的错误。 Property 'ResizeObserver' does not exist on type 'Global &amp; typeof globalThis' 嗨,是的,对我来说工作正常。如果我删除代码,我会收到此错误This browser does not support ResizeObserver out of the box. See: https://github.com/react-spring/react-use-measure/#resize-observer-polyfills。您是否删除了polyfill?与上面的代码一样,您不需要它。如果是这样,可能是版本问题。我正在使用 react-scripts 4.0.0 对不起,我刚刚忘记提到我得到的错误(如果我删除调整大小的观察者模拟)来自我在项目中使用的库(@visx/tooltip)。在您的情况下,错误可能会有所不同,因为您使用的是不同的库 我没有使用任何库,只是在自定义挂钩中使用了 resizeObserver API,因为不需要使用库。我正在使用 react-scripts 4.0.1 但错误更多是打字稿错误,而不是浏览器错误。 抱歉,上面提出的解决方案仅适用于 javascript。我刚刚找到了一个可以帮助here 的答案,如果你添加global.ResizeObserver = resizeObserverMock; 而不是const globalAny:any = global; globalAny.ResizeObserver = resizeObserverMock;。让我知道这是否有效,我将编辑我的答案【参考方案4】:

我已经在 setupTests.js/ts 中添加了下一个代码:

global.ResizeObserver = jest.fn().mockImplementation(() => (
    observe: jest.fn(),
    unobserve: jest.fn(),
    disconnect: jest.fn(),
))

【讨论】:

这对我来说在模板 js 中进行了开玩笑测试。谢谢

以上是关于如何使用反应测试库模拟 ResizeObserver 以在单元测试中工作的主要内容,如果未能解决你的问题,请参考以下文章

无法在反应测试库中使用模拟服务人员

获取模拟服务工作者的请求体和反应测试库

你们如何测试复杂的反应原生应用程序?

如何测试/模拟反应钩子?

如何使用打字稿为反应测试库设置智能感知

当它被包装在提供者组件中时,如何使用 mocha + 酶 + chai 测试反应原生组件