17

I am using React's TestUtil.renderIntoDocument to test a React component class, like this (only I am using TypeScript instead of Babel):

describe("MyComponent", () => {
  it("will test something after being mounted", () => {
    var component = TestUtils.renderIntoDocument(<MyComponent />);
    // some test...
  })
})

This works, but I want to write a test that verifies that componentWillUnmount behaves as expected. However, it seems that the test runner never unmounts the component, which is not surprising. So my question is: how do I unmount the component from within a test? The TestUtil doesn't have anything that looks like what I want, something along the lines of removeFromDocument I would imagine.

Aaron Beall
  • 49,769
  • 26
  • 85
  • 103
  • Possible duplicate of [Unmount / destroy Component in jsdom test](http://stackoverflow.com/questions/23973942/unmount-destroy-component-in-jsdom-test) – Henrik Andersson Dec 06 '15 at 03:35

5 Answers5

21

Using enzyme 3 library's shallow() and unmount(), you can test if lifecycle methods have been called like this:

it(`lifecycle method should have been called`, () => {
  const componentDidMount = jest.fn()
  const componentWillUnmount = jest.fn()

  // 1. First extends your class to mock lifecycle methods
  class Foo extends MyComponent {
    constructor(props) {
      super(props)
      this.componentDidMount = componentDidMount
      this.componentWillUnmount = componentWillUnmount
    }

    render() {
      return (<MyComponent />)
    }
  }

  // 2. shallow-render and test componentDidMount
  const wrapper = shallow(<Foo />)

  expect(componentDidMount.mock.calls.length).toBe(1)
  expect(componentWillUnmount.mock.calls.length).toBe(0)

  // 3. unmount and test componentWillUnmount
  wrapper.unmount()

  expect(componentDidMount.mock.calls.length).toBe(1)
  expect(componentWillUnmount.mock.calls.length).toBe(1)
})
bob
  • 2,674
  • 1
  • 29
  • 46
  • 1
    My original (very old) question was about Facebook's TestUtils, but indeed Enzyme is a way better solution and I've long since switched to use it instead. – Aaron Beall Jan 02 '18 at 15:37
  • Thanks so much! `wrapper.unmount()` is what I was looking for. Personally, I don't think it's worth testing that the React lifecycle methods work correctly, but instead, the test should check that the code *within* the lifecycle methods is behaving correctly. – Jay Jul 10 '19 at 11:20
  • @Jay I kind of agree. I am interested in moving to ‘react-testing-library’ that encourages not testing implementation details. – bob Jul 11 '19 at 21:30
14
Step1: Use "jest.spyOn" on "componentWillUnmount" method.
Step2: Trigger unmount on the mounted component.
Finally: check if componentWillUnmount is called when component is unmounted

Code

it('componentWillUnmount should be called on unmount', () => {
    const component = createComponent();
    const componentWillUnmount = jest.spyOn(component.instance(), 'componentWillUnmount');
    component.unmount();
    expect(componentWillUnmount).toHaveBeenCalled();
});
D P Venkatesh
  • 529
  • 7
  • 15
9

That's right but TestUtils.renderIntoDocument returns a ReactComponent with access to the lifecycle methods of the component.

So you can call manually component.componentWillUnmount().

ncuillery
  • 14,308
  • 2
  • 22
  • 31
  • 3
    In my case I had to call `component.instance().componentWillUnmount()`. Calling it directly on the component didn't work. – Daniel Sep 11 '18 at 09:43
2
import { mount } from 'enzyme';
import ReactDOM from 'react-dom';
...

let container;
beforeEach(() => {
   container = document.createElement("div");
   mount(<YourReactComponent />, {attachTo: container});
});

afterEach(() => {
    ReactDOM.unmountComponentAtNode(container);
});
Maciej Zawiasa
  • 189
  • 2
  • 6
  • Yes I've since switched to enzyme which solved this and a lot of other limitations with FB's test utils. :) – Aaron Beall Nov 23 '16 at 16:13
  • Thanks a lot for this answer. I've tried everything but only this solution worked. I don't know what exactly happens with Enzyme in order to need this kind of workaround. Thanks a lot. +1 – R. Karlus Apr 21 '20 at 23:50
0

Suppose you have a function call in componentWillUnmount:

componentWillUnmount(){
    this.foo();
}

In the test you can do:

const wrapper = mount(<YourComponent />);
const spy = jest.spyOn(wrapper.instance(), 'foo');
wrapper.unmount();
expect(spy).toHaveBeenCalled();
Mareș Ștefan
  • 430
  • 1
  • 4
  • 13