@testing-library/react-hooks 와 jest를 사용해서 react-hooks를 test 해보았다.
생각 보다 막히는 곳이 많았다. 그러한 막힌 부분들을 메모해두려고 한다.
infinite Scroll을 테스트 하고 싶었는데,
scrollTop은 변경할 수 있는 값이었지만, scrollHeight나 clientHeight 는 read-only 값이라 변경할 수가 없었다.
그리고 scroll event를 실행시키기가 어려웠다. 해당 문제는 다음과 같이 해결했다. (describe 안의 구문이다.)
jest.useFakeTimers();
const dispatchScrollEvent = ({
scrollHeight,
clientHeight,
scrollTop,
}: {
scrollHeight: number;
clientHeight: number;
scrollTop: number;
}) => {
Object.defineProperty(document.body, 'scrollHeight', {
value: scrollHeight,
writable: true,
});
Object.defineProperty(document.documentElement, 'clientHeight', {
value: clientHeight,
writable: true,
});
document.body.scrollTop = scrollTop;
window.dispatchEvent(new Event('scroll'));
jest.advanceTimersByTime(500);
};
document.body의 값들을 바꿀 수 있고, window.dispatchEvent로 event를 발생시켜서 hooks의 useEffect에서 걸어놓은 scroll event를 발생시킬 수 있었다. resize 이벤트 등도 비슷하게 발생시켜 테스트 했다.
reducer를 사용하여 data를 fetch해오는 hooks에서 loading 과 data, error 등을 관리할 수 있게 만들었다. 해당 hooks를 test 하기 위해서, teset의 callback에 async
와 await act
를 사용해야했다. 그렇지 않으면 에러가 발생했다.
it('fetch에 성공하면 state의 data가 바뀌어야한다.', async () => {
const spy = () => {
return Promise.resolve({ data: 'test' });
};
const { result } = renderHook(() => useAsync<{ test: string }, Error, () => Promise<any>>(spy));
let [state, fetch, setData] = result.current;
expect(state).toEqual({
loading: false,
data: null,
error: null,
});
await act(async () => {
await fetch();
});
state = result.current[0];
expect(state).toEqual({
loading: false,
data: { data: 'test' },
error: null,
});
debounce 등 때문에 setTimeout 후에 적용 되는 값들을 테스트 하기 위해서는
jest.useFakeTimers();
와
act(() => { jest.advanceTimersByTime(500); });
를 활용하여 테스트를 하였다. 그러면 해당 초 뒤에 rerendering 된 값으로 expect
할수 있었다.