10
votes

I hope someone can point me in the right direction to test useRef in the component below.

I have a component structured something like below. I am trying to test the functionality within the otherFunction() but I'm not sure how to mock the current property that comes off the component ref. Has anyone done something like this before?

const Component = (props) => {
    const thisComponent = useRef(null);
    const otherFunction = ({ current, previousSibling  }) => {
        if (previousSibling) return previousSibling.focus();
        if (!previousSibling && current) return current.focus();
    } 
    const handleFocus = () => {
        const {current} = thisComponent;
        otherFunction(current);
    }
     return (
        <div ref={thisComponent} onFocus={handleFocus}>Stuff In here</div>
    );
};
1
there is definitely no way to inject/modify function's closures(and thisComponent is available through closures in handleFocus). if .focus() is called properly to you(I mean there is no error) you may try to put mock on HTMLElement.prototype.focusskyboyer
This makes sense. Thanks for the input!Jordan Melendez

1 Answers

8
votes

Here is my test strategy for your case. I use jest.spyOn method to spy on React.useRef hook. It will let us mock the different return value of ref object for SFC.

index.tsx:

import React, { RefObject } from 'react';
import { useRef } from 'react';

export const Component = props => {
  const thisComponent: RefObject<HTMLDivElement> = useRef(null);
  const otherFunction = ({ current, previousSibling }) => {
    if (previousSibling) return previousSibling.focus();
    if (!previousSibling && current) return current.focus();
  };
  const handleFocus = () => {
    const { current } = thisComponent;
    const previousSibling = current ? current.previousSibling : null;
    otherFunction({ current, previousSibling });
  };
  return (
    <div ref={thisComponent} onFocus={handleFocus}>
      Stuff In here
    </div>
  );
};

index.spec.tsx:

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

describe('Component', () => {
  const focus = jest.fn();
  beforeEach(() => {
    jest.restoreAllMocks();
    jest.resetAllMocks();
  });
  test('should render correctly', () => {
    const wrapper = shallow(<Component></Component>);
    const div = wrapper.find('div');
    expect(div.text()).toBe('Stuff In here');
  });
  test('should handle click event correctly when previousSibling does not exist', () => {
    const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: { focus } });
    const wrapper = shallow(<Component></Component>);
    wrapper.find('div').simulate('focus');
    expect(useRefSpy).toBeCalledTimes(1);
    expect(focus).toBeCalledTimes(1);
  });

  test('should render and handle click event correctly when previousSibling exists', () => {
    const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: { previousSibling: { focus } } });
    const wrapper = shallow(<Component></Component>);
    wrapper.find('div').simulate('focus');
    expect(useRefSpy).toBeCalledTimes(1);
    expect(focus).toBeCalledTimes(1);
  });

  test('should render and handle click event correctly when current does not exist', () => {
    const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: null });
    const wrapper = shallow(<Component></Component>);
    wrapper.find('div').simulate('focus');
    expect(useRefSpy).toBeCalledTimes(1);
    expect(focus).not.toBeCalled();
  });
});

Unit test result with 100% coverage:

 PASS  src/stackoverflow/56739670/index.spec.tsx (6.528s)
  Component
    ✓ should render correctly (10ms)
    ✓ should handle click event correctly when previousSibling does not exist (3ms)
    ✓ should render and handle click event correctly when previousSibling exists (1ms)
    ✓ should render and handle click event correctly when current does not exist (2ms)

-----------|----------|----------|----------|----------|-------------------|
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:       4 passed, 4 total
Snapshots:   0 total
Time:        7.689s

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