如何测试这个自定义钩子(useRef)

How to test this custom hook (useRef)

我正在尝试测试这个自定义挂钩,但我不知道如何将 useRef 作为参数发送 ElementRef 正在使用 useRef

import { MutableRefObject, useEffect, useState } from "react";


export default function useNearScreen(elementRef: MutableRefObject<HTMLDivElement>, margin = 80) {

  const [show, setShow] = useState(false);

  useEffect(() => {
    const onChange = (entries: IntersectionObserverEntry[]) => {
        const el: IntersectionObserverEntry = entries[0];
        if (el.isIntersecting) {
            setShow(true)

            observer.disconnect();
        }

    }

    const observer = new IntersectionObserver(onChange, {
        rootMargin: `${margin}px`
    })

    observer.observe(elementRef.current as Element);

    return () => observer.disconnect();
  })
  return show;
 }

首先jest默认使用jsdom作为测试环境。 jsdom 不支持 IntersectionObserver,请参阅 issue#2032。所以我们需要模拟它并手动触发回调。

我将使用 @testing-library/react-hooks 包来测试 React 自定义挂钩。

例如

useNearScreen.ts:

import { MutableRefObject, useEffect, useState } from 'react';

export default function useNearScreen(elementRef: MutableRefObject<HTMLDivElement>, margin = 80) {
  const [show, setShow] = useState(false);

  useEffect(() => {
    const onChange = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
      const el: IntersectionObserverEntry = entries[0];
      if (el.isIntersecting) {
        setShow(true);

        observer.disconnect();
      }
    };

    const observer = new IntersectionObserver(onChange, {
      rootMargin: `${margin}px`,
    });

    observer.observe(elementRef.current as Element);

    return () => observer.disconnect();
  });
  return show;
}

useNearScreen.test.ts:

import { renderHook } from '@testing-library/react-hooks';
import { MutableRefObject } from 'react';
import { useRef } from 'react';
import useNearScreen from './useNearScreen';

describe('useNearScreen', () => {
  test('should pass', () => {
    const mObserver = {
      observe: jest.fn(),
      unobserve: jest.fn(),
      disconnect: jest.fn(),
    };
    const mIntersectionObserver = jest.fn();
    mIntersectionObserver.mockImplementation((callback, options) => {
      callback([{ isIntersecting: true }], mObserver);
      return mObserver;
    });
    window.IntersectionObserver = mIntersectionObserver;

    const mHTMLDivElement = document.createElement('div');
    const { result } = renderHook(() => {
      const elementRef = useRef<HTMLDivElement>(mHTMLDivElement);
      return useNearScreen(elementRef as MutableRefObject<HTMLDivElement>);
    });
    expect(result.current).toBe(true);
    expect(mIntersectionObserver).toBeCalledWith(expect.any(Function), { rootMargin: '80px' });
    expect(mObserver.observe).toBeCalledWith(mHTMLDivElement);
    expect(mObserver.disconnect).toBeCalled();
  });
});

测试结果:

 PASS  Whosebug/71118856/useNearScreen.test.ts (9.656 s)
  useNearScreen
    ✓ should pass (16 ms)

------------------|---------|----------|---------|---------|-------------------
File              | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------|---------|----------|---------|---------|-------------------
All files         |     100 |    66.67 |     100 |     100 |                   
 useNearScreen.ts |     100 |    66.67 |     100 |     100 | 9                 
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        10.581 s