wrapper.update()不应该在那里。它不会返回承诺,也不能被await编辑。默认情况下,酶已经运行了生命周期挂钩,包括componentDidMount。
这个问题是componentDidMount不可测试的,并且不能兑现承诺。应该将其提取为返回可以链接的promise的方法:
loadSomething() { const candidateId = this.props.match.params.candidate_id; return Promise.all([ this.props.expressApi.applicationsOfCandidate(candidateId), this.props.expressApi.candidateReviewStatus(candidateId), ]).then((data) => { const candidateInfo = data[0][this.props.match.params.candidate_id]; const reviewInfo = data[1][0]; this.setState({ ... }); }).catch((err) => { this.props.handleError(err); this.setState({error: err, }); });}componentDidMount() { this.loadSomething();}
然后可以将其存入一个测试中:
jest.stub(Candidate.prototype, ’loadSomething’)shallow(<Candidate .../>);expect(shallow.instance().loadSomething).toHaveBeenCalledTimes(1);
并直接在另一个中进行测试:
shallow(<Candidate .../>, { disableLifecycleMethods: true });await shallow.instance().loadSomething();expect(shallow.state(...))...解决方法
我对使用React应用程序上的酶/ Jest进行测试还很陌生,所以我设置测试的方式可能有些问题。
我特别想在组件中测试一个功能: reviewCandidate()
reviewCandidate() { const { candidate_id: candidateId,} = this.props.match.params; const { latest_application_id: latestApplication,} = this.state.candidateInfo; const { expressApi,} = this.props; /** @todo Define user when authentication is in place */ const user = ’undefined’; expressApi.addReviewedCandidate( parseInt(candidateId,10),latestApplication,user,// comment,).then(() => { // If the API call is successful,have the state reflect this accordingly this.setState({ reviewInfo: {candidate_id: candidateId,last_application_reviewed: latestApplication,// comment: comment },}); // Pop-up a snackbar indicating a successful review this.props.setSnackbar({ open: true,message: <span>Candidate_{candidateId} marked as reviewed</span>,}); });}
使用Web应用程序时,它可以正常工作而不会发出任何警告。当我尝试使用Jest / Enzyme测试它时,就会出现问题。
这是我为上述功能编写的测试:
describe(’Reviewing Candidates’,() => { const mockCandidateReviewStatus = []; const mockApplicationsOfCandidate = {/* mock data */}; const mockExpressApi = { applicationsOfCandidate() { return new Promise(resolve => resolve(mockApplicationsOfCandidate)); },candidateReviewStatus() { return new Promise(resolve => resolve(mockCandidateReviewStatus)); },addReviewedCandidate() { return new Promise(resolve => resolve()); },}; const handleError = error => error; it(’ensures reviewedCandidates() is called correctly’,async() => { const setSnackbar = jest.fn(() => ’foobar’); const wrapper = shallow( <CandidateexpressApi={mockExpressApi}handleError={handleError}match={{ params: { candidate_id: 153,},}}utils={utils}setSnackbar={setSnackbar} /> ); /** * These wrapper.update() calls ensure componentDidMount() finish setting * the state of the component * * Unsure if this is the best way to deal with waiting for * componentDidMount() to finish */ await wrapper.update(); await wrapper.update(); await wrapper.instance().reviewCandidate(); /** * @todo: Check this.state.reviewInfo is set as expected */ expect(setSnackbar).toHaveBeenCalled(); });});
但是await wrapper.instance().reviewCandidate()通话使酶崩溃。
Error: Method “setState” is only meant to be run on a single node. 3 found instead. at ShallowWrapper.single (/path/to/project/node_modules/enzyme/build/ShallowWrapper.js:1828:17) at ShallowWrapper.setState (/path/to/project/node_modules/enzyme/build/ShallowWrapper.js:575:14) at Candidate.ShallowWrapper.instance.setState (/path/to/project/node_modules/enzyme/build/ShallowWrapper.js:217:35) at expressApi.addReviewedCandidate.then (/path/to/project/src/components/Candidate.js:514:12) at <anonymous> at process._tickCallback (internal/process/next_tick.js:188:7)
如果我注释掉this.setState调用的.then()API调用后:
reviewCandidate() { // ... expressApi.addReviewedCandidate( // ... ).then(() => { // If the API call is successful,have the state reflect this accordingly /* this.setState({ reviewInfo: {candidate_id: candidateId,}); */ // Pop-up a snackbar indicating a successful review this.props.setSnackbar({ open: true,}); });}
然后测试可以正常进行:
PASS test/Candidate.test.js (15.954s) PASS test/Main.test.jsTest Suites: 2 passed,2 totalTests: 17 passed,17 totalSnapshots: 0 totalTime:20.378sRan all test suites related to changed files.----------------------------|----------|----------|----------|----------|-------------------|File| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |----------------------------|----------|----------|----------|----------|-------------------|All files | 31.05 | 24.58 | 23.04 | 31.88 | | src|0 |0 |0 |0 | | index.js |0 |0 |0 |0 | 1,2,3,4,5,6,7,9 | src/components | 43.93 | 38.41 | 33.86 | 44.92 | | Candidate.js | 62.41 | 60.87 | 50 | 62.59 |... 88,489,492,616 | Main.js | 92.31 | 88.89 | 79.31 | 92.21 |... 34,342,354,363 |----------------------------|----------|----------|----------|----------|-------------------|
万一有关系,这里是componentDidMount()在测试中调用Mock API调用的同一组件中的函数:
componentDidMount() { const candidateId = this.props.match.params.candidate_id; Promise.all([ this.props.expressApi.applicationsOfCandidate(candidateId),this.props.expressApi.candidateReviewStatus(candidateId),]).then((data) => { const candidateInfo = data[0][this.props.match.params.candidate_id]; const reviewInfo = data[1][0]; this.setState({ /* Sets various state parameters based on what was returned from the API calls*/ }); }).catch((err) => { this.props.handleError(err); this.setState({error: err,}); });}
我在做什么是造成这种情况的原因Error: Method “setState” is only meant to be run on a singlenode. 3 found instead.?
如果需要提供有关组件本身的更多信息,请告诉我。
在此先感谢您的帮助。