Home > OS >  How to test this custom hook (useRef)
How to test this custom hook (useRef)

Time:02-16

I'm trying to test this custom hook but I don't know how to send the useRef as a parameter ElementRef is using 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;
 }

CodePudding user response:

First of all, jest use jsdom as its test environment by default. jsdom doesn't support IntersectionObserver, see issue#2032. So we need to mock it and trigger the callback manually.

I will use @testing-library/react-hooks package to test react custom hook.

E.g.

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();
  });
});

Test result:

 PASS  stackoverflow/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
  • Related