Обновление состояния компонента React в тестах на жасмин
У меня есть относительно простой компонент React, который отображает список на основе его состояния. Затем у меня есть тест кармы/жасмина, который отображает компонент, устанавливает его состояние и проверяет, что отображается правильная разметка.
Проблема, с которой я сталкиваюсь, заключается в том, что каждый раз, когда я делаю setState({})
или forceUpdate()
на моем компоненте, я получаю сообщение об ошибке:
TypeError: 'undefined' is not an object (evaluating 'deepestAncestor.firstChild')
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10314
Каков правильный способ проверки изменений состояния в компоненте React?
Код ответа:
var NotificationCenter = React.createClass({
getInitialState: function(){
return {notifications:[]}
},
render: function() {
countContainerStyle = {
display: this.state.notifications.length > 0 ? '' : 'none'
};
return (
<div id="pc-notification-center">
<span className="pc-notification-center-bell" >
B
</span>
<span className="pc-notification-count-container" style={countContainerStyle}>
<span className="pc-notification-count-circle">●</span>
<span className="pc-notification-count">{this.state.notifications.length}</span>
</span>
</div>);
}
});
return NotificationCenter;
});
Тестовый код:
it('should set its notification count to the number of notifications it has', function() {
var notificationCenter = NotificationCenter({}),
countNode;
TestUtils.renderIntoDocument(notificationCenter);
notificationCenter.setState({
notifications: [1,2]
});
countNode = TestUtils.findRenderedDOMComponentWithClass(notificationCenter,'pc-notification-count');
expect(countNode).toBe(2);
});
Изменить: Полная трассировка стека
PhantomJS 1.9.7 (Linux) [object Object] [object Object] [object Object] should default its notification count to 0 FAILED
TypeError: 'undefined' is not an object (evaluating 'deepestAncestor.firstChild')
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10314
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10260
at getNode (/home/company/projects/user_interface_kit/bower_components/react/react.js:9874)
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4472
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:28
at /home/company/projects/user_interface_kit/bower_components/react/react.js:5925
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:75
at /home/company/projects/user_interface_kit/bower_components/react/react.js:5925
at /home/company/projects/user_interface_kit/.tmp/notification_center/popover.js:81
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10461
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11924
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13944
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13877
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4360
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10055
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11169
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10105
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11169
at /home/company/projects/user_interface_kit/.tmp/notification_center/notification_center.js:50
at /home/company/projects/user_interface_kit/bower_components/react/react.js:10461
at /home/company/projects/user_interface_kit/bower_components/react/react.js:11924
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13944
at /home/company/projects/user_interface_kit/bower_components/react/react.js:13877
at /home/company/projects/user_interface_kit/bower_components/react/react.js:4360
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:10483
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:11597
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:10533
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:11597
at /home/company/projects/user_interface_kit/bower_components/react/react-with-addons.js:12716
at /home/company/projects/user_interface_kit/test/notification_center/notification_center_test.js:19
at /home/company/projects/user_interface_kit/node_modules/karma-jasmine/lib/adapter.js:171
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1585
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:841
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1074
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:126
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1117
at each (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:58)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1118
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:895
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1104
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:754
at callGetModule (/home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1129)
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1479
at /home/company/projects/user_interface_kit/node_modules/karma-requirejs/lib/require.js:1606
Вот небольшой проект, воспроизводящий проблему
https://github.com/treehau5/react_karma_requirejs_bug_reproduction
Ответы
Ответ 1
После глубокого изучения того, как работают React и React_with_addons, похоже, что локальный объект React в тесте отличается от экземпляра React, который использует React_with_addons. Поэтому, если вы сделаете это в своем тесте:
react_with_addons.addons.TestUtils.renderIntoDocument(componentInstance);
Затем action_with_addon экземпляр React регистрирует компонентInstance и пронумерует его объект nodeCache. Местный экземпляр React остается нетронутым. Затем, если вы попытаетесь обновить состояние компонента следующим образом:
componentInstance.updateState({key:'newValue'});
Локальный экземпляр React используется для обновления DOM. Поскольку никакие компоненты никогда не монтировались с этим экземпляром, обновление не выполняется, и вы получаете "undefined" не объект (оценивающий ошибку "deepestAncestor.firstChild" ).
Интересно, что если вы монтируете какой-либо компонент в локальный React, тогда вы не увидите ошибку при обновлении состояния компонента, даже если этот компонент был смонтирован с помощью объекта React object_with_addon.
Лучший способ избежать этой проблемы на данный момент, если ваш тест должен запускать setState, заключается в том, чтобы просто не использовать функцию response_with_addon renderIntoDocument. Вместо этого просто создайте свой собственный. Его единственные две строки:
function renderIntoDocument(instance) {
var div = document.createElement('div');
return React.renderComponent(instance, div);
};
Ответ 2
Я не знаю, верен ли предыдущий ответ.
С React 0.11.1 этот код отлично работает:
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
jest.dontMock('public/components/MyThing.jsx');
var MyThing = require('public/components/MyThing.jsx');
describe('MyThing', function() {
var html;
describe('#render', function() {
beforeEach(function(){
var component = MyThing();
var componentInstance = TestUtils.renderIntoDocument(component);
componentInstance.setState({isCool: true});
html = componentInstance.getDOMNode().textContent;
});
it('includes something cool', function(){
expect(html).toContain('something cool');
});
});
});
Ответ 3
Вы все еще можете использовать оригинальный TestUtils.renderIntoDocument, только нужно сделать это, чтобы переместить требуемый React в beforeEach. Поскольку, если вам требуется React глобально, контекст будет отличаться для каждого теста, что заставляет два разных экземпляра монтировать один и тот же компонент.
beforeEach(function () {
React = require('react/addons');
TestUtils = React.addons.TestUtils;
});
Ответ 4
Это исходный код renderIntoDocument (React 0.14.8, который я использую в проекте):
https://github.com/facebook/react/blob/v0.14.8/src/test/ReactTestUtils.js#L79
И вот когда это было изменено - более 2 лет назад: https://github.com/facebook/react/commit/ce95c3d042309d8aced894cc6be43d7e4cf96455
Поэтому, несмотря на имя, оно фактически ничего не выводит в документ.
Я также могу подтвердить, что ответ Assaf все еще работает на 0.14.8 - в основном, напишите свой собственный renderIntoDocument. Вот то, что сработало для меня:
function renderIntoDocument (instance) {
var div = document.createElement('div');
document.documentElement.appendChild(div);
return ReactDOM.render(instance, div);
}