Spying on React functional component method with jest and enzyme; Cannot spyOn on a primitive value

I am trying to test a React component and make sure that when its button gets clicked, the correct method gets invoked. However, when I try to run my test and try to spy on that method, I get the following message:

Error: Cannot spyOn on a primitive value; undefined given

How do I test that when a button is clicked the correct method is invoked? Thanks!

sampleComponent.jsx:

import * as React from 'react';

const SampleComponent = () => {
  const sampleMethod = () => {
    console.log('hello world');
  };

  return <button onClick={sampleMethod} type="button">Click Me</button>;
};

export default SampleComponent;

sampleComponent.test.jsx:
import * as React from 'react';
import { shallow } from 'enzyme';
import SampleComponent from './sample';

test('testing spy', () => {
  const spy = jest.spyOn(SampleComponent.prototype, 'sampleMethod');
  const wrapper = shallow(<SampleComponent />);
  wrapper.find('button').simulate('click');
  expect(spy).toHaveBeenCalled();
});

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The error means, the function sampleMethod you defined inside the functional component SampleComponent is not defined in SampleComponent.prototype. So SampleComponent.prototype.sampleMethod is undefined, jest can’t spy on a undefined value.

So the correct way to test sampleMethod event handler is like this:

index.spec.tsx:

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

describe('SampleComponent', () => {
  test('should handle click correctly', () => {
    const logSpy = jest.spyOn(console, 'log');
    const wrapper = shallow(<SampleComponent></SampleComponent>);
    const button = wrapper.find('button');
    expect(button.text()).toBe('Click Me');
    button.simulate('click');
    expect(logSpy).toBeCalledWith('hello world');
  });
});

We can spy on console.log, to assert it is to be called or not.

Unit test result with 100% coverage:

 PASS  src/react-enzyme-examples/02-react-hooks/index.spec.tsx
  SampleComponent
    ✓ should handle click correctly (19ms)

  console.log node_modules/jest-mock/build/index.js:860
    hello world

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

Dependencies version:

"react": "^16.11.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
"jest": "^24.9.0",
"jest-environment-enzyme": "^7.1.1",
"jest-enzyme": "^7.1.1",

Method 2

sample.js

import * as React from 'react';

export let util = {sampleMethod: null };

const SampleComponent = () => {
  util.sampleMethod = () => {
    console.log('hello world');
  };

  return <button onClick={sampleMethod} type="button">Click Me</button>;
};

export default SampleComponent;

sample.test.js

import { shallow } from 'enzyme';
import SampleComponent, {util} from './sample';

test('testing spy', () => {
  const spy = jest.spyOn( util, 'sampleMethod' );
  const wrapper = shallow(<SampleComponent />);
  wrapper.find('button').simulate('click');
  expect(spy).toHaveBeenCalled(1);
});

I know I’m late to answer but I think this would help some other developers also

Method 3

Also searching for a way on spying on a function inside a functional component, it seems just not possible to be done nicley (booo!). I didn’t want to spy on a console log, the 2nd suggestion using an object defined outside the fc I was not able to get it working.

I came up with a solution which is also not nice, but simple and may help others with this problem.
It is NOT spying on the function, which was asked for, but the outcome is may be close enough for some scenarios.

MyFC.tsx

const MyFC = ({callback}: {callback?:()=>void}) => {
    const handleMyClick = 
        callback // this is the mock fn
        || ()=> console.log("do stuff") // this would be the regular implementation

    return <button onClick={handleMyClick}>Click Me</button>
}

MyFC.test.tsx
it('should be triggered', () => {
    const mockFn = jest.fn();
    const wrapper = mount(<MyFC callback={mockFn}/>);
    wrapper.find('button').simulate('click')
    expect(mockFn).toBeCalled()        
}

with this approach you can at least test if it’s called, with what arguments etc
If somebody finds a way to do this properly, I would be glad to hear…


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x