Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking refs in React function component

I have React function component that has a ref on one of its children. The ref is created via useRef.

I want to test the component with the shallow renderer. I have to somehow mock the ref to test the rest of the functionality.

I can't seem to find any way to get to this ref and mock it. Things I have tried

  • Accessing it via the childs property. React does not like that, since ref is not really a props

  • Mocking useRef. I tried multiple ways and could only get it to work with a spy when my implementation used React.useRef

I can't see any other way to get to the ref to mock it. Do I have to use mount in this case?

I can't post the real scenario, but I have constructed a small example

it('should test', () => {
    const mock = jest.fn();
    const component = shallow(<Comp onHandle={mock}/>);


    // @ts-ignore
    component.find('button').invoke('onClick')();

    expect(mock).toHaveBeenCalled();
});

const Comp = ({onHandle}: any) => {
    const ref = useRef(null);

    const handleClick = () => {
        if (!ref.current) return;

        onHandle();
    };

    return (<button ref={ref} onClick={handleClick}>test</button>);
};
like image 614
Shadowlauch Avatar asked Sep 05 '19 12:09

Shadowlauch


People also ask

Can we use refs in functional component?

When the ref attribute is used on a custom class component, the ref object receives the mounted instance of the component as its current . You may not use the ref attribute on function components because they don't have instances.

How do you reference a mock React?

To mock React useRef or a function inside a functional component with enzyme and Jest, we can mock the react module by mocking the useRef hook and getting the rest of the mocked module from the real module. import React, { useRef } from "react"; import { shallow } from "enzyme"; import Child2 from "./"; jest.

How are refs used in functional component React?

To create a ref in a functional component we use the useRef() hook which returns a mutable object with a . current property set to the initialValue we passed to the hook. This returned object will persist for the full lifetime of the component. Thus, throughout all of its re-rendering and until it unmounts.

How do refs React to components?

In order to get a reference to a React component, you can either use this to get the current React component, or you can use a ref to get a reference to a component you own. They work like this: var MyComponent = React. createClass({ handleClick: function() { // Explicitly focus the text input using the raw DOM API.


1 Answers

Here is my unit test strategy, use jest.spyOn method spy on the useRef hook.

index.tsx:

import React from 'react';

export const Comp = ({ onHandle }: any) => {
  const ref = React.useRef(null);

  const handleClick = () => {
    if (!ref.current) return;

    onHandle();
  };

  return (
    <button ref={ref} onClick={handleClick}>
      test
    </button>
  );
};

index.spec.tsx:

import React from 'react';
import { shallow } from 'enzyme';
import { Comp } from './';

describe('Comp', () => {
  afterEach(() => {
    jest.restoreAllMocks();
  });
  it('should do nothing if ref does not exist', () => {
    const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: null });
    const component = shallow(<Comp></Comp>);
    component.find('button').simulate('click');
    expect(useRefSpy).toBeCalledWith(null);
  });

  it('should handle click', () => {
    const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: document.createElement('button') });
    const mock = jest.fn();
    const component = shallow(<Comp onHandle={mock}></Comp>);
    component.find('button').simulate('click');
    expect(useRefSpy).toBeCalledWith(null);
    expect(mock).toBeCalledTimes(1);
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/57805917/index.spec.tsx
  Comp
    ✓ should do nothing if ref does not exist (16ms)
    ✓ should handle click (3ms)

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |      100 |      100 |      100 |      100 |                   |
 index.tsx |      100 |      100 |      100 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.787s, estimated 11s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57805917

like image 171
slideshowp2 Avatar answered Oct 29 '22 23:10

slideshowp2