Enzyme: Unmount is not working as described in the docs

Created on 23 Jan 2017  ·  5Comments  ·  Source: enzymejs/enzyme

I was experiencing some unintuitive behavior from unmount. I took the example from the docs:

it('Calls componentWillUnmount after unmount', () => {
    const willMount = sinon.spy();
    const didMount = sinon.spy();
    const willUnmount = sinon.spy();

    class Foo extends React.Component {
      constructor(props) {
        super(props);
        this.componentWillUnmount = willUnmount;
        this.componentWillMount = willMount;
        this.componentDidMount = didMount;
      }
      render() {
        return (
          <div className={this.props.id}>
            {this.props.id}
          </div>
        );
      }
    }
    const wrapper = mount(<Foo id="foo" />);
    expect(willMount.callCount).toEqual(1);
    expect(didMount.callCount).toEqual(1);
    expect(willUnmount.callCount).toEqual(0);
    wrapper.unmount();
    expect(willMount.callCount).toEqual(1);
    expect(didMount.callCount).toEqual(1);
    expect(willUnmount.callCount).toEqual(1);
  })

And got the following result:

  ● <SurveyEditor/> › Test

    expect(received).toEqual(expected)

    Expected value to equal:
      1
    Received:
      0

      at Object.<anonymous> (client/pages/__tests__/SurveyEditor.jsx:180:35)

Line 180 corresponds to: expect(willUnmount.callCount).toEqual(1); It seems as though componentWillUnmount is not actually getting called. Relevant dependencies include:

  • "enzyme": "^2.7.0",
  • "jest": "^18.1.0",
  • "jest-cli": "^18.1.0",
  • "sinon": "^1.17.7",
  • "babel-jest": "^18.0.0",
mount bug

Most helpful comment

Thanks for the repo! I was able to reproduce your issue with it. That is super weird. Those tests appear to be fully isolated (mounting two distinct components) but removing the first test cases the second to pass again 😕

The stack trace for the error seems to indicate it has something to do with your Flux dispatcher:

Stack trace:

at Object.<anonymous> (test.js:60:35)
      at Store.<anonymous> (test.js:25:7)
      at emitNone (events.js:86:13)
      at Store.emit (events.js:185:7)
      at Store._this.save (stores/Store.js:7:39)
      at Object.Store._this.handleActions (stores/Store.js:13:43)
      at Dispatcher._invokeCallback (node_modules/flux/lib/Dispatcher.js:198:24)
      at Dispatcher.dispatch (node_modules/flux/lib/Dispatcher.js:174:14)
      at Object.save (actions/Actions.js:4:50)
      at onClick (test.js:14:228)
      at Object.invokeGuardedCallback [as invokeGuardedCallbackWithCatch] (node_modules/react-dom/lib/ReactErrorUtils.js:26:5)
      at executeDispatch (node_modules/react-dom/lib/EventPluginUtils.js:83:21)
      at Object.executeDispatchesInOrder (node_modules/react-dom/lib/EventPluginUtils.js:108:5)
      at executeDispatchesAndRelease (node_modules/react-dom/lib/EventPluginHub.js:43:22)
      at executeDispatchesAndReleaseSimulated (node_modules/react-dom/lib/EventPluginHub.js:51:10)
      at forEachAccumulated (node_modules/react-dom/lib/forEachAccumulated.js:26:8)
      at Object.processEventQueue (node_modules/react-dom/lib/EventPluginHub.js:255:7)
      at node_modules/react-dom/lib/ReactTestUtils.js:340:22
      at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-dom/lib/Transaction.js:140:20)
      at Object.batchedUpdates (node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:62:26)
      at Object.batchedUpdates (node_modules/react-dom/lib/ReactUpdates.js:97:27)
      at node_modules/react-dom/lib/ReactTestUtils.js:338:18
      at ReactWrapper.<anonymous> (node_modules/enzyme/build/ReactWrapper.js:776:11)
      at ReactWrapper.single (node_modules/enzyme/build/ReactWrapper.js:1421:25)
      at ReactWrapper.simulate (node_modules/enzyme/build/ReactWrapper.js:769:14)
      at Object.<anonymous> (test.js:27:28)
      at process._tickCallback (internal/process/next_tick.js:103:7)


I suspect it's because of the first test being asynchronous and the second is synchronous. The test passes if you make the second test asynchronous and delay the mount assertions until the next tick:

process.nextTick(() => {
  const wrapper = mount(<Foo id="foo" />);
  expect(willMount.callCount).toEqual(1);
  expect(didMount.callCount).toEqual(1);
  expect(willUnmount.callCount).toEqual(0);
  wrapper.unmount();
  expect(willMount.callCount).toEqual(1);
  expect(didMount.callCount).toEqual(1);
  expect(willUnmount.callCount).toEqual(1);
  done()
})

All 5 comments

@ml9951 I tried to reproduce using the same version of Enzyme, but everything worked for me. You can see my test here: https://github.com/aweary/enzyme-test-repo/blob/issue-785/test.js

What version of React are you using?

That is interesting. I tried your example and it is working for me. I also replaced your package.json with the one from my other project and it still worked after deleting node_modules and reinstalling everything. It seems like one of my other tests in that same describe is somehow interfering with it. If I comment out all of my other tests in that file, it works fine. This doesn't really make any sense to me, given that this is a very simple and isolated test that doesn't depend on any stores/actions or anything else that has anything to do with my other tests. I'll see if I can simplify it and get back to you with a reproducible example.

OK, I've managed to reproduce it in a fork of your repo: https://github.com/ml9951/enzyme-test-repo/tree/issue-785. Installing the dependencies and running npm test should reproduce it. I'm testing the flow of a stripped down flux app. Unless I'm missing something, it shouldn't have any impact on the original test that I posted. If you switch the order of the two tests, then they both pass.

Thanks for the repo! I was able to reproduce your issue with it. That is super weird. Those tests appear to be fully isolated (mounting two distinct components) but removing the first test cases the second to pass again 😕

The stack trace for the error seems to indicate it has something to do with your Flux dispatcher:

Stack trace:

at Object.<anonymous> (test.js:60:35)
      at Store.<anonymous> (test.js:25:7)
      at emitNone (events.js:86:13)
      at Store.emit (events.js:185:7)
      at Store._this.save (stores/Store.js:7:39)
      at Object.Store._this.handleActions (stores/Store.js:13:43)
      at Dispatcher._invokeCallback (node_modules/flux/lib/Dispatcher.js:198:24)
      at Dispatcher.dispatch (node_modules/flux/lib/Dispatcher.js:174:14)
      at Object.save (actions/Actions.js:4:50)
      at onClick (test.js:14:228)
      at Object.invokeGuardedCallback [as invokeGuardedCallbackWithCatch] (node_modules/react-dom/lib/ReactErrorUtils.js:26:5)
      at executeDispatch (node_modules/react-dom/lib/EventPluginUtils.js:83:21)
      at Object.executeDispatchesInOrder (node_modules/react-dom/lib/EventPluginUtils.js:108:5)
      at executeDispatchesAndRelease (node_modules/react-dom/lib/EventPluginHub.js:43:22)
      at executeDispatchesAndReleaseSimulated (node_modules/react-dom/lib/EventPluginHub.js:51:10)
      at forEachAccumulated (node_modules/react-dom/lib/forEachAccumulated.js:26:8)
      at Object.processEventQueue (node_modules/react-dom/lib/EventPluginHub.js:255:7)
      at node_modules/react-dom/lib/ReactTestUtils.js:340:22
      at ReactDefaultBatchingStrategyTransaction.perform (node_modules/react-dom/lib/Transaction.js:140:20)
      at Object.batchedUpdates (node_modules/react-dom/lib/ReactDefaultBatchingStrategy.js:62:26)
      at Object.batchedUpdates (node_modules/react-dom/lib/ReactUpdates.js:97:27)
      at node_modules/react-dom/lib/ReactTestUtils.js:338:18
      at ReactWrapper.<anonymous> (node_modules/enzyme/build/ReactWrapper.js:776:11)
      at ReactWrapper.single (node_modules/enzyme/build/ReactWrapper.js:1421:25)
      at ReactWrapper.simulate (node_modules/enzyme/build/ReactWrapper.js:769:14)
      at Object.<anonymous> (test.js:27:28)
      at process._tickCallback (internal/process/next_tick.js:103:7)


I suspect it's because of the first test being asynchronous and the second is synchronous. The test passes if you make the second test asynchronous and delay the mount assertions until the next tick:

process.nextTick(() => {
  const wrapper = mount(<Foo id="foo" />);
  expect(willMount.callCount).toEqual(1);
  expect(didMount.callCount).toEqual(1);
  expect(willUnmount.callCount).toEqual(0);
  wrapper.unmount();
  expect(willMount.callCount).toEqual(1);
  expect(didMount.callCount).toEqual(1);
  expect(willUnmount.callCount).toEqual(1);
  done()
})

Had the same issue. In my case simply making all of the tests asynchronous did the trick to have componentWillUnmount be called as expected

Was this page helpful?
0 / 5 - 0 ratings