Jest / Enzyme - 生命周期方法中的模拟异步函数
Posted
技术标签:
【中文标题】Jest / Enzyme - 生命周期方法中的模拟异步函数【英文标题】:Jest / Enzyme - mock async function in lifecycle method 【发布时间】:2019-01-24 11:38:38 【问题描述】:我正在尝试使用 Jest 对我的 React 组件进行单元测试。我的测试通过了,但由于 try/catch 中的 console.error() 而出现 TypeError。我认为我的模拟设置不正确,但我尝试在没有任何结果的情况下异步进行。我很感激任何提示。
console.error 组件/App.js:91 TypeError:无法读取未定义的属性“fetchPoints” 在 App.fetchPoints (D:\\components\App.js:87:48) 在 tryCatch (D:\\node_modules\regenerator-runtime\runtime.js:62:40) 在 Generator.invoke [as _invoke] (D:\\node_modules\regenerator-runtime\runtime.js:296:22) 在 Generator.prototype.(anonymous function) [as next] (D:\\node_modules\regenerator-runtime\runtime.js:114:21) 在步骤 (D:\\components\App.js:38:191) 在 D:\\components\App.js:38:437 在新的承诺 () 在应用程序。 (D:\\components\App.js:38:99) 在 App.componentDidMount (D:\\components\App.js:155:30) 在 D:\\node_modules\react-test-renderer\lib\ReactCompositeComponent.js:262:25 在 measureLifeCyclePerf (D:\\node_modules\react-test-renderer\lib\ReactCompositeComponent.js:73:12) 在 D:\\node_modules\react-test-renderer\lib\ReactCompositeComponent.js:261:11 在 CallbackQueue.notifyAll (D:\\node_modules\react-test-renderer\lib\CallbackQueue.js:74:22) 在 ReactTestReconcileTransaction.close (D:\\node_modules\react-test-renderer\lib\ReactTestReconcileTransaction.js:34:26) 在 ReactTestReconcileTransaction.closeAll (D:\\node_modules\react-test-renderer\lib\Transaction.js:207:25) 在 ReactTestReconcileTransaction.perform (D:\\node_modules\react-test-renderer\lib\Transaction.js:154:16) 在 batchedMountComponentIntoNode (D:\\node_modules\react-test-renderer\lib\ReactTestMount.js:67:27) 在 ReactDefaultBatchingStrategyTransaction.perform (D:\\node_modules\react-test-renderer\lib\Transaction.js:141:20) 在 Object.batchedUpdates (D:\\node_modules\react-test-renderer\lib\ReactDefaultBatchingStrategy.js:60:26) 在 Object.batchedUpdates (D:\\node_modules\react-test-renderer\lib\ReactUpdates.js:95:27) 在 Object.render (D:\\node_modules\react-test-renderer\lib\ReactTestMount.js:126:18) 在 Object.create (D:\\components__tests__\App.test.js:26:31) 在 Object.asyncJestTest (D:\\node_modules\jest-jasmine2\build\jasmine_async.js:108:37) 解决时 (D:\\node_modules\jest-jasmine2\build\queue_runner.js:56:12) 在新的承诺 () 在映射器 (D:\\node_modules\jest-jasmine2\build\queue_runner.js:43:19) 在 promise.then (D:\\node_modules\jest-jasmine2\build\queue_runner.js:87:41) 在 在 process._tickCallback (internal/process/next_tick.js:188:7)
App.test.js
我尝试使用 mount() 和 fetchPoints 来返回 Promise.resolve()。
import React from 'react';
import renderer from 'react-test-renderer';
import shallow, configure, mount from 'enzyme';
import Adapter from 'enzyme-adapter-react-15';
import App from '../App';
configure( adapter: new Adapter() );
describe('App', () =>
let wrapper;
const apiMockActions =
fetchPoints : jest.fn()
;
beforeEach(() =>
wrapper = shallow(<App actions=apiMockActions />);
);
it('should call fetchPoints in #componentDidMount', () =>
return wrapper.instance().componentDidMount().then(() =>
expect(apiMockActions.fetchPoints).toHaveBeenCalled();
);
);
);
App.js
import React, Component from 'react';
import connect from 'react-redux';
import * as api from '../actions/api';
import bindActionCreators from 'redux';
export class App extends Component
constructor(props)
super(props);
this.state =
data: []
async componentDidMount()
try
let res = await this.props.actions.fetchPoints();
//this.setState( data: res );
catch (error)
console.error(error);
render()
return (
<div className="col-md-12 app__div">
</div>
);
function mapStateToProps(state)
return
data: state.points
;
function mapDispatchToProps(dispatch)
return
actions: bindActionCreators(Object.assign(, api), dispatch)
;
export default connect(mapStateToProps, mapDispatchToProps)(App);
package.json
"dependencies":
"ag-grid": "^14.2.0",
"ag-grid-react": "^14.2.0",
"axios": "^0.16.2",
"babel-eslint": "^8.2.6",
"babel-polyfill": "^6.26.0",
"chart.js": "^2.7.1",
"classnames": "^2.2.5",
"eslint": "^5.2.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-react": "^7.10.0",
"lodash": "^4.15.0",
"moment": "^2.19.3",
"prop-types": "^15.6.0",
"rc-time-picker": "^2.4.1",
"react": "^15.6.2",
"react-addons-css-transition-group": "^15.6.2",
"react-autosuggest": "^9.3.2",
"react-bootstrap": "^0.31.5",
"react-color": "^2.14.0",
"react-datalist": "^4.0.0",
"react-datepicker": "^0.29.0",
"react-dom": "^15.6.2",
"react-dom-factories": "^1.0.2",
"react-dropzone": "^3.13.4",
"react-grid-layout": "^0.15.3",
"react-intl": "^2.4.0",
"react-notification-system": "^0.2.16",
"react-notify": "^2.0.1",
"react-redux": "^5.0.6",
"react-timepicker": "^1.3.1",
"react-toggle-display": "^2.2.0",
"redux": "^3.7.2",
"redux-logger": "^3.0.6",
"redux-promise-middleware": "^4.4.2",
"redux-thunk": "^2.2.0",
"section-iterator": "^2.0.0",
"style-loader": "^0.13.2"
,
"devDependencies":
"babel-core": "^6.26.0",
"babel-jest": "^23.4.0",
"babel-loader": "^7.1.2",
"babel-plugin-lodash": "^3.3.2",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"chalk": "^2.3.0",
"cross-env": "^5.1.1",
"css-loader": "^0.23.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-15": "^1.0.6",
"jest": "^23.4.1",
"jest-cli": "^23.4.1",
"parallel-webpack": "^1.5.0",
"progress-bar-webpack-plugin": "^1.11.0",
"react-test-renderer": "^15.6.2",
"redux-immutable-state-invariant": "^2.1.0",
"style-loader": "^0.13.1",
"webpack": "^4.16.1",
"webpack-cli": "^3.0.8"
,
【问题讨论】:
您使用的是旧版本的react
,可以分享一下您的package.json
吗?
【参考方案1】:
应该为每个测试创建间谍和存根:
let apiMockActions;
beforeEach(() =>
apiMockActions =
fetchPoints : jest.fn()
;
);
在 Enzyme 3 及更高版本中默认启用生命周期,因此手动调用 componentDidMount
会导致调用它两次。他们can be disabled 是为了链接componentDidMount
的承诺:
beforeEach(() =>
wrapper = shallow(<App actions=apiMockActions />, disableLifecycleMethods: true );
);
返回的承诺值可以用mockResolvedValue
模拟:
it('should call fetchPoints in #componentDidMount', async () =>
const points = [...];
const instance = wrapper.instance();
apiMockActions.fetchPoints.mockResolvedValue(points);
await instance.componentDidMount();
expect(apiMockActions.fetchPoints).toHaveBeenCalled();
expect(instance.state.data).toEqual( data: points );
);
【讨论】:
仍然收到此控制台消息。所有测试都通过了。 console.log components/App.js:38 无法读取未定义的属性“fetchPoints” 我更新了答案。但是,如果您包含建议的beforeEach
,我不确定如何取消定义 actions
。我建议调试您的代码并确保apiMockActions
是(<App actions=apiMockActions />
中的一个对象。
一切都好。 console.log(typeof instance.props.actions) outputs: object
fetchPoints 函数也在实例中定义。当我从 try/catch 中删除 console.error(e) 语句时,它不会输出“未定义”。我很好奇为什么会调用 catch 块,因为我定义了解析值。
当我定义 mockRejectedValue('403') 时,它会按预期在控制台中输出 '403'。 fetchPoints undefiend 没有出现。但是,当我没有定义 mockRejectedValue 时,我仍然不知道为什么在 catch 块中调用 fetchPoints() 函数。我认为它应该输出“错误”未定义。我会在 Jest 文档中做一些研究。感谢您的帮助!
它输出未定义,因为this.props.actions
未定义并导致错误。我认为这与 mockRejectedValue 无关,一个模拟在 OP 中设置不正确,但不会导致此错误。您提供的代码无法解释它。由于actions
明确提供为shallow(<App actions=apiMockActions />, ...)
,这可能意味着App
在其他地方被实例化。还要仔细检查apiMockActions
是否是您调用shallow
的对象。以上是关于Jest / Enzyme - 生命周期方法中的模拟异步函数的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Jest - Enzyme 测试 React 中 mapStateToProps 中的方法
Jest/Enzyme 使用 try/catch 中的 async/await 方法测试 React 组件
测试包含在 withRouter 中的反应组件(最好使用 jest/enzyme)
在使用 Jest/Enzyme 进行测试期间检测 React 中的合成点击