Jest/Enzyme 错误:“方法 'setState' 仅用于在单个节点上运行.找到了 3 个." [英] Jest/Enzyme Error: "Method 'setState' is only meant to run on a single node. 3 found instead."
问题描述
我对在 React 应用程序上使用 Enzyme/Jest 进行测试还很陌生,所以我设置测试的方式可能有问题.
I'm fairly new to testing with Enzyme/Jest on a React application, so perhaps there's something wrong with the way I'm setting up my test.
我特别想在我的组件中测试单个函数:reviewCandidate()
I specifically want to test a single function in my component: 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,
user,
// 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 对其进行测试时出现问题.
It works without any warnings when using the web application. The issue arises when I try to test it using Jest/Enzyme.
这是我为上述函数编写的测试:
Here is the test I have written for the above function:
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(
<Candidate
expressApi={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()
调用使酶崩溃.
But the await wrapper.instance().reviewCandidate()
call crashes Enzyme.
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)
如果我在 API 调用之后注释掉 .then()
中的 this.setState
调用:
If I comment out the this.setState
call in the .then()
after the API call:
reviewCandidate() {
// ...
expressApi.addReviewedCandidate(
// ...
).then(() => {
// If the API call is successful, have the state reflect this accordingly
/*
this.setState({
reviewInfo: {
candidate_id: candidateId,
last_application_reviewed: latestApplication,
user,
// comment: comment
},
});
*/
// Pop-up a snackbar indicating a successful review
this.props.setSnackbar({
open: true,
message: <span>Candidate_{candidateId} marked as reviewed</span>,
});
});
}
然后测试没有任何问题:
Then the test works without any issues:
PASS test/Candidate.test.js (15.954s)
PASS test/Main.test.js
Test Suites: 2 passed, 2 total
Tests: 17 passed, 17 total
Snapshots: 0 total
Time: 20.378s
Ran 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 |
----------------------------|----------|----------|----------|----------|-------------------|
如果相关,这里是在测试中调用 Mock API 调用的同一组件中的 componentDidMount()
函数:
In-case it is relevant, here is the componentDidMount()
function in the same component that calls the Mock API calls in the test:
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,
});
});
}
我在做什么导致了错误:方法setState"只能在单个节点上运行.找到了 3 个.
?
如果我需要提供有关组件本身的更多信息,请告诉我.
Please let me know if I need to provide more information about the component itself.
在此先感谢您的帮助.
推荐答案
wrapper.update()
不应该在那里.它不返回承诺并且不能被await
ed.Enzyme 已经默认运行生命周期钩子,包括 componentDidMount
.
wrapper.update()
shouldn't be there. It doesn't return a promise and cannot be await
ed. Enzyme already runs lifecycle hooks by default, including componentDidMount
.
问题在于 componentDidMount
不可测试且不公开承诺.它应该被提取到一个返回一个可以链接的承诺的方法:
The problem is componentDidMount
isn't testable and doesn't expose a promise. It should be extracted to a method that returns a promise that could be chained:
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();
}
然后它可以在一个测试中被截断:
Then it can be stubbed in one test:
jest.stub(Candidate.prototype, 'loadSomething')
shallow(<Candidate .../>);
expect(shallow.instance().loadSomething).toHaveBeenCalledTimes(1);
并直接在另一个中测试:
And tested directly in another:
shallow(<Candidate .../>, { disableLifecycleMethods: true });
await shallow.instance().loadSomething();
expect(shallow.state(...))...
这篇关于Jest/Enzyme 错误:“方法 'setState' 仅用于在单个节点上运行.找到了 3 个."的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!