How to mock useRef hook with react-native using enzyme

Viewed 1343

I would like to write tests for my React-native app. My parent component will execute the methods within the child component. My child component is using the Hooks forwardRef, useImperativeHandle, Ref as seen below

childs.tsx

export interface RefChild {
    toggle: () => void,
    close: () => void
}

const Child = forwardRef((props: ChildProps, ref: Ref<RefChild>) => {
    const [isVisible, setIsVisible] = useState(false);
    useImperativeHandle(ref, () => ({ toggle, close }));

    const toggle = () => {
        setIsVisible(!isVisible);
    }

    const close = () => {
        setIsVisible(false)
    }

    return (...mycomponent)
}

My Parent component is catching the 'ref' call with

ref={(el: RefChild) => childRef.current = el}

Which allows me to call the 'toggle' and 'close' methods from within the Parent. Now, I fail to understand how to do the same thing within my test

my parent-test.tsx:

describe('Parent', () => {
    let wrapper: ShallowWrapper;
    let props: any;
    beforeEach(() => {
        props = createTestProps({});
        wrapper = shallow(<Parent {...props} />);
    });

//this is what I am currently trying to do, but not working
//test 1 (not working)
    it("useRef child", () => {
        const useRefSpy = jest.spyOn(React, 'useRef').mockReturnValueOnce({ current: <Child/> });
        expect(useRefSpy).toBeCalled();
        useRefSpy.current.toggle();
    })

//test 2 (not working)
    it("useRef child2", () => {
        const ref = {
            current: {
                toggle: jest.fn(),
                close: jest.fn()
            }
        }

        ref.current.toggle();
    })

//test 3 (not working)
    it("useRef child3", () => {
        wrapper.instance().childref.current.toggle(); //failing as functional components don't have instance
    })
})

My versions of React and RN are:

    "react": "16.13.1",
    "react-native": "0.63.3"

Could anyone explain me how should I achieve this?

1 Answers

As you mentioned in your question there is no instance in functional component, I think there is a better way to handle toggle and close functions from parent component using a boolean prop for each of them and the listen to changes in this value like this:

you have a state in parent component called isClose set to false and then in child component you use something like this:

useEffect(() => {
   if(isClose){
     //call close function
     close()
   }
}, [isClose])

But by the way in your current setup I think you need to mock the useRef hook something like this:


const useRefSpy = jest
  .spyOn(React, "useRef")
  .mockReturnValueOnce(() => ({ current: <Child /> }));
Related