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
にその余分なティック/サイクルを完了させることです。これはあまりにもハッキーに見えます。
私が試したもう1つのことは、アサーションをsetImmediate()
でラップすることです。これは、アサーションが通過する限り正常に機能します。失敗した場合、キャッチされなかったエラーのためにテスト全体が終了します。
誰かがこの問題を克服しましたか?
これが私がそれを解決した方法です。それが誰かを助けることを願っています。
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();
});
});