1

I'm trying to test React components with Enzyme + Sinon spies. My react code uses the property initializer syntax in my component instance:

class FixedButton extends React.Component {

    handleMouseOver = (e) => {
        const { fixedButtonHover = _fixedButtonHover } = this.props;
        const { backgroundColor, color } = fixedButtonHover;
        e.target.style.backgroundColor = backgroundColor;
        e.target.style.color = color;
    }
}

And I'm trying to figure out how to spy on the handleMouseOver function, although from what I understand, since the context is bound to the instance and not a property on the prototype, I cannot spy on it.

I know the function is called and I know I can make the spies work properly by modifying my syntax to standard property style. I also know that the mounted instance of the component has the method as a property.

Is this one of those cases where the two just won't work together, or is there a trick I've failed to see?

Edit: here's how I tried the suggested solution, but the spy.calledOnce returns false as does spy.called:

test('FixedButton component methods are called as expected', t => {

    const wrapper = mount(<FixedButton />);
    const instance = wrapper.instance();
    const spy = sinon.spy(instance, 'handleMouseOver');

    const button = wrapper.find('button');
    button.simulate('mouseover');

    t.is(spy.calledOnce, true);
})
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
STR.split
  • 23
  • 5

1 Answers1

0

Prototype methods with bind are preferable for several reasons including testability, they can be spied or mocked before class instantiation. This is important for methods that are called in constructor or initial lifecycle hooks.

handleMouseOver is clearly event handler that is triggered after component instantiation, so it can be spied, as long as component instance is reachable via Enzyme. A component needs to be re-rendered after setting up a spy in order for <button> to receive a new event handler. There's a bug with Enzyme update(), it needs to be worked around:

const instance = wrapper.instance();
const spy = sinon.spy(instance, 'handleMouseOver');
instance.forceUpdate(); // necessary for button to receive proper handler

const button = wrapper.find('button');
button.simulate('mouseover');

t.is(spy.calledOnce, true);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Thanks for answering, appreciate the explanation as well. I tried this syntax and while the error doesn't throw, the spy doesn't report the correct results of how many times it was called. Despite logging inside the handleMouseOver function. – STR.split Oct 27 '18 at 21:31
  • Please, provide https://stackoverflow.com/help/mcve in the question how exactly you tried it. – Estus Flask Oct 27 '18 at 21:37
  • Updated the answer, it was interesting to learn about `wrapper.update()` bug. Even if you tried `update` here, it wouldn't work. As for me, I'm using prototype methods with `bind` all the way. It's less of a concern in React but still a prototype provides extra layer of abstraction. – Estus Flask Oct 28 '18 at 06:58
  • Thank you! Solved it. Very odd issue. – STR.split Oct 28 '18 at 14:48