-
통과하면 안 되는 테스트인데 자꾸 통과하는 이유는... (await waitFor)Today I Learned 2022. 11. 24. 00:11
다음의 소스코드와 테스트 코드를 보자.
로그인 화면에서 아이디와 비밀번호를 입력한 후 로그인 버튼을 누르면 userStore에서 API를 호출하는 함수인 login 함수를 호출하는지 테스트하는 동작이다.
// LoginPage.jsx const login = async (data) => { const { identifier, password } = data; const verifiedAccessToken = await userStore.login({ identifier, password }); if (verifiedAccessToken) { // navigate } };
// LoginPage.test.jsx context('폼을 채우고 로그인 버튼을 누르면', () => { login = jest.fn(); it('userStore의 login 메서드를 호출', () => { renderLoginPage(); fireEvent.change(screen.getByLabelText('아이디'), { target: { value: 'hsjkdss228' }, }); fireEvent.change(screen.getByLabelText('비밀번호'), { target: { value: 'Password!1' }, }); fireEvent.click(screen.getByText('로그인')); waitFor(() => { expect(login).toBeCalledTimes(1); }); }); });
성공하는 것을 확인할 수 있다.
이게 진짜 성공하는 것인지 아닌지 한 번 검증이 필요할 것 같아 실패해야 하는 테스트 코드로 수정한 뒤 테스트 코드를 다시 실행했다.
waitFor(() => { expect(login).toBeCalledTimes(20); });
로그인 메서드를 20번을 호출할 것이라는 당연히 실패해야 하는 테스트가 성공하는 말도 안 되는 모습이 나타났다.
API 요청을 모두 수행한 뒤 통과해야 하는 비동기 메서드의 동작이나 모의 반환값을 테스트할 때는 일정 시간을 기다렸다가 테스트를 수행하는 waitFor 메서드를 사용하면 된다고 생각했는데, 왜 이런 의도하지 않은 결과를 반환하는지 이유를 찾아보았다.
3기 후배님의 도움을 받아 waitFor가 어떻게 구성되어 있는지 찾아보았다.
// wait-for.d.ts export function waitFor<T>( callback: () => Promise<T> | T, options?: waitForOptions, ): Promise<T>
waitFor 내부에 정의되어 있는 콜백 함수가 Promise 형태를 반환하는 것으로 확인되었다. Promise가 테스트에 영향을 끼치는지 확인하기 위해 자료를 찾던 중 다음 글을 확인할 수 있었다.
'Async methods without await' 부분을 참고했다.
설명에 따르면, jest는 한 구문의 실행 결과로 Promise만 존재하는 경우 Promise의 결과를 기다리지 않고 다음 테스트를 진행한다. 즉, waitFor 내부에 작성했던 테스트 코드들은 실제로는 성공한 것이 아니라, 결과가 나올 때까지 기다리지 않고 무시하고 다음 구문으로 넘어갔던 것이었다.
waitFor 구문에 await를 붙여줌으로써 Promise의 결과를 기다리게 하니 테스트를 정상적으로 수행하는 것을 확인할 수 있었다.
context('폼을 채우고 로그인 버튼을 누르면', async () => { login = jest.fn(); it('userStore의 login 메서드를 호출', () => { renderLoginPage(); fireEvent.change(screen.getByLabelText('아이디'), { target: { value: 'hsjkdss228' }, }); fireEvent.change(screen.getByLabelText('비밀번호'), { target: { value: 'Password!1' }, }); fireEvent.click(screen.getByText('로그인')); await waitFor(() => { expect(login).toBeCalledTimes(20); }); }); });
'Today I Learned' 카테고리의 다른 글
테스트 코드가 아무리 많아도 진짜 필요한 것을 테스트하지 않는다면? (게시글 작성하기) (0) 2022.11.25 React DatePicker 라이브러리를 사용해 날짜 선택하기 (0) 2022.11.24 이제야 조금은 앱 같다 (0) 2022.11.22 미신청자/신청자/참가자/작성자 별 컴포넌트 구분하기 (0) 2022.11.20 하나의 작업이 세 개의 작업으로 분신술을 쓰는 기적 (0) 2022.11.19