JestでcomponentDidMountにsetStateを含んだテストをする
履歴ReactコンポーネントでcomponentDidMount内でsetStateが動くようなのをテストしようとしてハマりました。
例えば以下のような感じでテストしても動かないので悩んでいました。
Test.tsx
import * as React from 'react';
import * as request from 'request-promise-native';
interface Props { };
interface State {
time: string,
};
class Test extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
time: '',
};
}
render() {
return (
<div>
{ this.state.time }
</div>
);
}
async componentDidMount() {
const time = await request.get('localhost', {}, (err, res, body) => {
return body;
}
);
this.setState({time: time});
}
}
export default Test;
Test.test.tsx
import * as React from 'react';
import * as request from 'request-promise-native';
import { render } from '@testing-library/react';
import Test from './Test';
test('render test', () => {
const mock_get = jest.spyOn(request, 'get');
mock_get.mockResolvedValue(new Promise((resolve, reject) => {
resolve('23:15:18');
}));
const c = render(<Test />);
setImmediate(() => {
console.log(c.debug());
expect(c.getByText('23:15:18')).toBeInTheDocument(); // この時点でstate.timeは''のまま
}
);
});
Enzymeがあればイケるのか?とか色々試したんですが、全然うまく動かなくて、ググってみればstack overflowに答えが。
最後のチェックの部分を以下のように setImmediate
でラップすればいいみたい。
setImmediate(() => {
expect(getByText('23:15:18')).toBeInTheDocument(); // この時点でstate.timeは''のまま
});
結局のところ Promiseが非同期で動くんだけど、いつ動くかわからないのに手続き的に書いても動いてなくて、
setImmediate
で非同期処理が終わったあとのサイクルで expect(...)
を動かすようにするということ…でいいんでしょうか。