I faced a problem with my jest+enzyme mount()
testing. I am testing a function, which switches displaying components.
Switch between components: when state.infoDisplayContent = 'mission'
a missionControl
component is mounted, when state.infoDisplayContent = 'profile'
- other component steps in:
_modifyAgentStatus () {
const { currentAgentProfile, agentsDatabase } = this.state;
const agentToMod = currentAgentProfile;
if (agentToMod.status === 'Free') {
this.setState({
infoDisplayContent: 'mission'
});
agentToMod.status = 'Waiting';
} else if (agentToMod.status === 'Waiting') {
const locationSelect = document.getElementById('missionLocationSelect');
agentToMod.location = locationSelect[locationSelect.selectedIndex].innerText;
agentToMod.status = 'On Mission';
this.setState({
infoDisplayContent: 'profile'
});
}
}
When I trigger this function everything looks Ok, this test runs well and test successfully pass with required component:
import React from 'react';
import { mount } from 'enzyme';
import App from '../containers/App';
const result = mount(
<App />
)
test('change mission controls', () => {
expect(result.state().currentAgentProfile.status).toBe('Free');
result.find('#statusController').simulate('click');
expect(result.find('#missionControls')).toHaveLength(1);
expect(result.find('#missionLocationSelect')).toHaveLength(1);
expect(result.state().currentAgentProfile.status).toBe('Waiting');
});
But when I simulate onClick two times:
test('change mission controls', () => {
expect(result.state().currentAgentProfile.status).toBe('Free');
result.find('#statusController').simulate('click');
expect(result.find('#missionControls')).toHaveLength(1);
expect(result.find('#missionLocationSelect')).toHaveLength(1);
expect(result.state().currentAgentProfile.status).toBe('Waiting');
result.find('#statusController').simulate('click');
expect(result.state().currentAgentProfile.status).toBe('On Mission');
});
I get this assert:
TypeError: Cannot read property 'selectedIndex' of null
at App._modifyAgentStatus (development/containers/App/index.js:251:68)
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:350: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:348: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> (development/tests/AgentProfile.test.js:26:38)
at process._tickCallback (internal/process/next_tick.js:109:7)
It is obvious that:
document.getElementById('missionLocationSelect');
return null, but I can not get why. Element passes tests, as I mention.
expect(result.find('#missionLocationSelect')).toHaveLength(1);
But it could not be captured with document.getElementById()
.
Please, help me to fix this problem and run tests.
Jest is a unit test framework designed by Facebook to test react applications. Jest allows to write unit tests using the Snapshot feature. Enzyme allows to write unit tests using regular assertions. Using Enzyme with Jest makes writing tests for React applications a lot easier.
Shallow rendering is one way that Enzyme keeps tests simpler than Jest. When you shallow-render a component with Enzyme, you render only that component. Enzyme doesn't render any of the children of that component. This is a useful restriction that ensures that you aren't testing too much in one test.
Always begin with shallow. If componentDidMount or componentDidUpdate should be tested, use mount. If you want to test component lifecycle and children behavior, use mount. If you want to test children rendering with less overhead than mount and you are not interested in lifecycle methods, use render.
Found the solution thanks to https://stackoverflow.com/users/853560/lewis-chung and gods of Google:
Attached my component to DOM via attachTo
param:
const result = mount( <App />, { attachTo: document.body } );
Changed buggy string in my method to string which works with element Object
agentToMod.location = locationSelect.options[locationSelect.selectedIndex].text;` : _modifyAgentStatus () { const { currentAgentProfile, agentsDatabase } = this.state; const agentToMod = currentAgentProfile; if (agentToMod.status === 'Free') { this.setState({ infoDisplayContent: 'mission' }); agentToMod.status = 'Waiting'; } else if (agentToMod.status === 'Waiting') { const locationSelect = document.getElementById('missionLocationSelect'); agentToMod.location = agentToMod.location = locationSelect.options[locationSelect.selectedIndex].text; agentToMod.status = 'On Mission'; this.setState({ infoDisplayContent: 'profile' }); } }
attachTo: document.body
will generate a warning:
Warning: render(): Rendering components directly into document.body is discouraged, since its children are often manipulated by third-party scripts and browser extensions. This may lead to subtle reconciliation issues. Try rendering into a container element created for your app.
So just attach to a container element instead of document.body
, and no need to add it to the global Window object
before(() => {
// Avoid `attachTo: document.body` Warning
const div = document.createElement('div');
div.setAttribute('id', 'container');
document.body.appendChild(div);
});
after(() => {
const div = document.getElementById('container');
if (div) {
document.body.removeChild(div);
}
});
it('should display all contents', () => {
const wrapper = mount(<YourComponent/>,{ attachTo: document.getElementById('container') });
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With