How to sinon spy a React component constructor - unit testing?

4.4k Views Asked by At

I'm using enzyme, sinon and expect to unit test my react component.

import React from 'react';
import expect  from 'expect.js';
import { shallow } from 'enzyme';

import ExampleComponent from './../../../src/js/components/example-component';

describe('Test <ExampleComponent />', function() {

beforeEach(function() {
    this._sandbox = sinon.sandbox.create();
    this.constructorSpy = this._sandbox.spy(ExampleComponent.prototype, 'constructor');
});

afterEach(function() {
    this._sandbox.restore();
    this.constructorSpy = null;
});

it('Should set the state with the correct data [constructor]', function() {
    const wrapper = shallow(<ExampleComponent />);
    console.log(' - callCount: ', this.constructorSpy.callCount);
    expect(this.constructorSpy.calledOnce).to.be(true);

    expect(Immutable.is(wrapper.state('shownData'), Immutable.Map())).to.be(true);
});

I have some logic in my component constructor that sets the state depending on what I pass in as props. However, this test keeps telling me that the constructor call count is 0 and it is not called.

What's the correct way to spy on a component constructor? What am I doing wrong?

I am using a sandbox because there are other functions I want to add to the sandbox to spy on in the future.

1

There are 1 best solutions below

0
On

When enzyme shallow renders the test, the constructor (along with any other lifecycle methods) should be called automatically. Trying to overwrite it in a test could be incredibly complicated and not necessary for what you're trying to check.

If the constructor is setting the state based on the props, why is checking the resulting state in the test not sufficient? (see the 'Very Important' assertion in my example below)

Another option: Presumably you are setting items in the state that are then used in the component render - in which case you can just check for these items in the rendered element.

Lastly, I also included a function call in the constructor that I then spy on in the test (using sinon) to assert that it is called, in case it is helpful.

Example React component:

import React, { Component, PropTypes } from 'react'

export default class Post extends Component {
  static propTypes = {
    markRead: PropTypes.func.isRequired,
    postName: PropTypes.string.isRequired
  }

  constructor (props) {
    super(props)
    const { markRead, postName } = props

    markRead()
    this.state = {
      postName: 'Very Important: ' + postName
    }
  }

  render () {
    const { postName } = this.state

    return (
      <div className='post-info'>
        This is my post: {postName}!
      </div>
    )
  }
}

Example Enzyme test, which all passes:

import React from 'react'
import assert from 'assert'
import { shallow } from 'enzyme'
import { spy } from 'sinon'

import Post from 'client/apps/spaces/components/post'

describe('<Post />', () => {
  const render = (props) => {
    const withDefaults = {
      markRead: () => {},
      postName: 'MyPostName',
      ...props
    }
    return shallow(<Post {...withDefaults} />)
  }

  it('renders and sets the post name', () => {
    const markReadSpy = spy()
    const props = {
      markRead: markReadSpy
    }
    const wrapper = render(props)
    const postInfo = wrapper.find('.post-info')
    const postText = postInfo.text()

    assert.equal(wrapper.state('postName'), 'Very Important: MyPostName')
    assert(markReadSpy.calledOnce)
    assert.equal(postInfo.length, 1)
    assert(postText.includes('MyPostName'))
  })
})

Note: additionally, it looks as though you are not importing sinon in your example above fyi.