vamsiampolu vamsiampolu - 13 days ago 9
Javascript Question

Testing changes to React component state and spying on instance methods using enzyme

I am working on a wrapper component for smoothly loading images in React. I use enzyme with mocha, chai and sinon to unit test my component. In the test here, I am trying to test that the:


  1. component's state is updated when the image has loaded

  2. the
    onLoad
    instance method on the component was called




const wrapper = shallow( );

const onLoad = wrapper.find('img').props().onLoad;
const onLoadSpy = sinon.spy(onLoad); wrapper.update();
const status = wrapper.state().status;
expect(onLoadSpy).to.have.been.called;
expect(status).to.equal('LOADED');


I find that neither the update to the state is reflected by enzyme or the call count of the
onLoad
spy is updated. This is the corresponding code for the test:

export default class Image extends Component {
constructor(props) {
super(props);
if (props.src != null && typeof props.src === 'string') {
this.state = {
status: LOADING,
};
} else {
this.state = {
status: PENDING,
};
}
this.onLoad = this.onLoad.bind(this);
}

onLoad() {
this.setState({
status: LOADED,
});
}

render() {
//lots of code above the part we care about
const defaultImageStyle = style({
opacity: 0,
transisition: 'opacity 150ms ease',
});

const loadedImageStyle = style({
opacity: 1,
});

let imageStyle = defaultImageStyle;

if (this.state.status === LOADED) {
imageStyle = merge(defaultImageStyle, loadedImageStyle);
} else {
imageStyle = defaultImageStyle;
}


let image;
if (alt != null) {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
alt={alt}
onLoad={this.onLoad}
/>);
} else {
image = (<img
className={imageStyle}
src={src}
width={width}
height={height}
role="presentation"
onLoad={this.onLoad}
/>);
}

let statusIndicator = null;
if (this.state.status === LOADING) {
statusIndicator = (<div className={loadingStyle}></div>);
}

return (<div className={wrapperStyle}>
{statusIndicator}
{image}
</div>);

}}


To take a look at the full code for better context:


Answer

One can test this without relying on sinon. By expecting that the onLoad and onFire event listeners are invoked,the tests check if the img fires the load and error events.

Instead,simulate img's events using enzyme and check that the appropriate state transition occurs:

it('has a state of LOADED if a good src prop is supplied', () => {
  const wrapper = shallow(<Image 
    src="anyString.jpg"
    width={300}
    height={300}
  />);

  const img = wrapper.find('img');
  img.simulate('load');
  const status = wrapper.state().status;
  expect(status).to.equal('LOADED');
});

This also eliminates the need to mount the component. The updated tests can be found here.