question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

spy on wrapper.instance() method not called when using shallow()

See original GitHub issue

Testing a component method in isolation can be done via wrapper.instance().method(), see #208.

Testing a user event actually calls that method can be achieved using jest.spyOn() and .simulate().

To the best of my knowledge, you can spy on either the prototype or instance:

  • jest.spyOn(Component.prototype, 'method')
  • jest.spyOn(wrapper.instance(), 'method')

It seems that the prototype spy will work if you render with either shallow() or mount(), but when using the instance, mount() works and shallow() does not.

This would be fine, just use prototype right! Unfortunately, our team is lazy and don’t enjoy binding every method to this in the constructor(), so we use class properties to avoid it. The downside of class properties is that they do not appear on the prototype, so we are forced to spy on the instance. This works perfectly if you render with mount() but in unit tests, we always use shallow() so we can test the “unit” in isolation.

Can you see the dilemma? Should shallow() treat spies differently than mount()?

To demonstrate the issue below is a component and the associated test:

class App extends Component {
  constructor(...args) {
    super(...args);
    this.handleButtonClick = this.handleButtonClick.bind(this);
  }

  handleButtonClick(event) {
    // prototype method
  }

  handleAnchorClick = (event) => {
    // class property
  };

  render() {
    return (
      <div>
        <button onClick={this.handleButtonClick}>Click Me!</button>
        <a href="#" onClick={this.handleAnchorClick}>Click Me!</a>
      </div>
    );
  }
}
describe('spy using prototype', () => {
  it('calls "handleButtonClick()" on button click - using prototype', () => {
      const spy = jest.spyOn(App.prototype, 'handleButtonClick');
      const wrapper = shallow(<App />);
      wrapper.find('button').simulate('click', 'using prototype');
      expect(spy).toHaveBeenCalled();
    });

    // FAILS due to class properties not being on prototype
    it('calls "handleAnchorClick()" on anchor click - using prototype', () => {
      const spy = jest.spyOn(App.prototype, 'handleAnchorClick');
      const wrapper = shallow(<App />);
      wrapper.find('a').simulate('click', 'using prototype');
      expect(spy).toHaveBeenCalled();
    });
});

describe('spy using instance with mount', () => {
  it('calls "handleButtonClick()" on button click', () => {
    const wrapper = mount(<App />);
    const spy = jest.spyOn(wrapper.instance(), 'handleButtonClick');
    wrapper.update();
    wrapper.find('button').simulate('click');
    expect(spy).toHaveBeenCalled();
  });

  it('calls "handleAnchorClick()" on button click', () => {
    const wrapper = mount(<App />);
    const spy = jest.spyOn(wrapper.instance(), 'handleAnchorClick');
    wrapper.update();
    wrapper.find('a').simulate('click');
    expect(spy).toHaveBeenCalled();
  });
});

// FAILS due to shallow(), not sure why but mount() works as you can see above
describe('spy using instance with shallow', () => {
  it('calls "handleButtonClick()" on button click', () => {
    const wrapper = shallow(<App />);
    const spy = jest.spyOn(wrapper.instance(), 'handleButtonClick');
    wrapper.update();
    wrapper.find('button').simulate('click');
    expect(spy).toHaveBeenCalled();
  });

  it('calls "handleAnchorClick()" on button click', () => {
    const wrapper = shallow(<App />);
    const spy = jest.spyOn(wrapper.instance(), 'handleAnchorClick');
    wrapper.update();
    wrapper.find('a').simulate('click');
    expect(spy).toHaveBeenCalled();
  });
});

If you want to try it out, take a look at this repo. Any help will be greatly appreciated.

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Reactions:38
  • Comments:48 (28 by maintainers)

github_iconTop GitHub Comments

60reactions
iwllyucommented, Aug 14, 2017

@samit4me in your examples if you replace wrapper.update() when shallow rendering with wrapper.instance().forceUpdate() the failing tests will work

see: https://github.com/airbnb/enzyme/issues/622

@ljharb

If you use class properties instead of proper manual this-binding, you’re just stuck.

I’ve been following your advice on constructor binding and it’s performance issues but it seems to conflict with what they’re saying here https://github.com/facebook/react/issues/9851#issuecomment-306221157

tldr; performance wise, doing a constructor bind is identical to having a class property, with the added benefit of also being on the prototype (and the “performance” hit that comes with it)

54reactions
ljharbcommented, May 16, 2017

If you use class properties instead of proper manual this-binding, you’re just stuck.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How do I correctly spyOn a react component's method via the ...
When I try to spyOn the class prototype or wrapper.instance() I get Error: Cannot spy the searchBooks property because it is not a...
Read more >
Developers - spy on wrapper.instance() method not called when ...
spy on wrapper.instance() method not called when using shallow() ... Testing a component method in isolation can be done via wrapper.instance().method() , see...
Read more >
Shallow Rendering API - Enzyme - GitHub Pages
Returns the instance of the root component. .update() => ShallowWrapper. Syncs the enzyme component tree snapshot with the react component tree. .debug() => ......
Read more >
cannot spy the property because it is not a function; - You.com
Since it is a connected component, using shallowWithIntl() and dive() resolved ... the button const instance = wrapper.dive().instance(); const spy = jest.
Read more >
enzymejs/enzyme - Gitter
spyOn(wrapper.instance(), `onBlurMethod') ... oh, no - don't spy on that ... @ljharb I was fixing my tests to use shallow instead of mount...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found