在我的create-react-app中,我正在尝试测试一个在安装时执行多个setState
的组件。
class MyComponent extends React.Component {
state = {
a: undefined,
b: undefined,
c: undefined,
};
fetchA() {
// returns a promise
}
fetchB() {
// returns a promise
}
fetchC() {
// returns a promise
}
async componentDidMount() {
const a = await fetchA();
this.setState({ a });
}
async componentDidUpdate(prevProps, prevState) {
if (prevState.a !== this.state.a) {
const [b, c] = await Promise.all([
this.fetchB(a);
this.fetchC(a);
]);
this.setState({ b, c });
}
}
...
}
在我的测试中,我做了类似这样的事情,尝试在setState
中完成componentDidUpdate
之前完成断言。
import { mount } from 'enzyme';
describe('MyComponent', () => {
const fakeA = Promise.resolve('a');
const fakeB = Promise.resolve('b');
const fakeC = Promise.resolve('c');
MyComponent.prototype.fetchA = jest.fn(() => fakeA);
MyComponent.prototype.fetchB = jest.fn(() => fakeB);
MyComponent.prototype.fetchC = jest.fn(() => fakeC);
it('sets needed state', async () => {
const wrapper = mount(<MyComponent />);
await Promise.all([ fakeA, fakeB, fakeC ]);
expect(wrapper.state()).toEqual({
a: 'a',
b: 'b',
c: 'c',
});
});
});
以下是有趣的部分:上面的测试将失败,因为在进行断言时,最后一次setState
调用(在componentDidUpdate
中)尚未完成。那时,state.a
已设置,但state.b
和state.c
尚未设置。
我能使它发挥作用的唯一方法是在断言之前将await Promise.resolve(null)
楔入,以便给最后一个setState
额外的滴答/周期完成。这看起来太黑了。
我尝试的另一件事是在setImmediate()
中包装断言,只要断言通过就可以正常工作。如果失败,它将因未捕获的错误而终止整个测试。
有没有人克服过这个问题?
答案 0 :(得分:4)
这就是我解决它的方式。希望有所帮助。
import { mount } from 'enzyme';
describe('MyComponent', () => {
const fakeA = Promise.resolve('a');
const fakeB = Promise.resolve('b');
const fakeC = Promise.resolve('c');
MyComponent.prototype.fetchA = jest.fn(() => fakeA);
MyComponent.prototype.fetchB = jest.fn(() => fakeB);
MyComponent.prototype.fetchC = jest.fn(() => fakeC);
it('sets needed state', async (done) => {
const wrapper = mount(<MyComponent />);
await Promise.all([ fakeA, fakeB, fakeC ]);
setImmediate(() => {
// Without the try catch, failed expect will cause the
// whole test to crash out.
try {
expect(wrapper.state()).toEqual({
a: 'a',
b: 'b',
c: 'c',
});
} catch(error) {
done.fail(error);
}
done();
});
});