在 Jasmine 测试中更新 React 组件状态
Posted
技术标签:
【中文标题】在 Jasmine 测试中更新 React 组件状态【英文标题】:Updating React component state in Jasmine Test 【发布时间】:2014-04-23 04:15:03 【问题描述】:我有一个相对简单的 React 组件,它根据其状态呈现一个列表。然后我有一个 karma/jasmine 测试来呈现组件,设置它的状态,并检查是否呈现了正确的标记。
我遇到的问题是,每次我在组件上执行 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
【问题讨论】:
你能发布更多的堆栈跟踪吗?测试的哪一行导致抛出异常? 一个最小的复制案例(在 jsbin 等中)会有所帮助;你不应该得到那个错误。随意在 react repo 上提交错误。 我添加了完整的堆栈跟踪。导致异常的行是 .setState()。如果我手动更新状态对象并在 forceUpdate 调用上调用 forceUpdate(),我会得到同样的错误。当组件尝试重新渲染自身时,似乎会发生此错误。 @BenAlpert 我无法轻松制作 jsbin/fiddle,但我创建了一个重现该问题的小项目。很简单github.com/treehau5/react_karma_requirejs_bug_reproduction 【参考方案1】:这是 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);
【讨论】:
有趣的是,它可能不会使 他们的 测试失败,但它确实让我的测试失败(因为我的代码期望element.ownerDocument.body.contains(element)
为所有渲染元素返回 true)。【参考方案2】:
你仍然可以使用原来的TestUtils.renderIntoDocument,只需要将需要的React 移到beforeEach 中。因为如果你需要全局 React,每个测试的上下文都会不同,这会导致 2 个不同的实例挂载同一个组件。
beforeEach(function ()
React = require('react/addons');
TestUtils = React.addons.TestUtils;
);
【讨论】:
这个答案在调试了将近一周后才救了我:)【参考方案3】:我不知道之前的答案是否仍然正确。
使用 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');
);
);
);
【讨论】:
谁能验证这是否仍然适用于0.12.*
?【参考方案4】:
在深入研究了 React 和 React_with_addons 的工作原理之后,看起来测试中的本地 React 对象与 React_with_addons 使用的 React 实例不同。因此,如果您在测试中这样做:
react_with_addons.addons.TestUtils.renderIntoDocument(componentInstance);
然后 react_with_addon 的 React 实例注册 componentInstance 并初始化它的 nodeCache 对象。本地 React 实例保持不变。然后,如果您尝试像这样更新组件的状态:
componentInstance.updateState(key:'newValue');
React 的本地实例用于尝试更新 DOM。由于此实例从未挂载过任何组件,因此更新失败,您会收到“未定义”不是对象(正在评估“deepestAncestor.firstChild”)错误。
有趣的是,如果您将任何组件安装到本地 React 中,那么在更新组件的状态时您不会看到错误,即使该组件是使用 react_with_addon 的 React 对象安装的。
如果您的测试需要运行 setState,目前避免此问题的最佳方法是不使用 react_with_addon 的 renderIntoDocument 函数。相反,只需创建自己的。它只有两行:
function renderIntoDocument(instance)
var div = document.createElement('div');
return React.renderComponent(instance, div);
;
【讨论】:
这仍然是有效的黑客攻击吗?或者现在有什么好的解决方法? 我猜这不再有效了,因为自编写此答案以来,React 经历了许多变化。以上是关于在 Jasmine 测试中更新 React 组件状态的主要内容,如果未能解决你的问题,请参考以下文章
Karma + Browserify + Jasmine + 伊斯坦布尔 + React 覆盖
如何用 Karma,Jasmine,Webpack 测试 UI 组件系列 配置篇
使用 Jasmine 间谍进行 Angular 2 组件测试“没有 Http 提供者!”错误