act 경고문 도대체 너는 누구냐...

YEONGHUN KO·2022년 8월 24일
0

JAVASCRIPT TESTING

목록 보기
8/11
post-thumbnail
post-custom-banner

..was not wrapped inside act

useReducer - dispatch를 쓰다가 발견한 경고문이지만 이때까지 수없이 접했던 경고문이다. (애증의 경고문)

우선 act가 무슨 함수인지 알아보자.

act

When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface.

React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.

act() makes sure that anything that might take time - rendering, user events, data fetching - within it is completed before test assertions are run.

해석해보면 유닛과 관련된 업데이트가 다 처리되어 DOM에 반영될때까지 기다려주는 역할을 한다. 이벤트를 발생시키거나 데이터를 fetching하는 것과 같은 작업을 기다리는 것이다.

그리고 나서 kent 블로그를 읽어보면 act 경고문이 나는 이유를 아래와 같이 정리할 수 있다.

아직 컴포넌트의 모든 use case를 다 테스트 하지 않았다는 것을 알려준다. 그리고 비동기 업데이트 같은 경우 react call stack이 처리하지 못한다. 그래서 act 경고문이 떴을 경우 거의 대부분, 테스트가 끝나고 나서 더 처리해야하거나 기다려야할 작업이 남았다는 것이고, 그걸 테스트 하는 코드를 짜지 않았기 때문이다.

If you're still experiencing the act warning, then the most likely reason is something is happening after your test completes for which you should be waiting

이때 act를 사용해서 비동기 업데이트도 테스트를 하게 해준다.

예를들어 아래와 같은 코드가 있다고 해보자.

function UsernameForm({ updateUsername }) {
  const [{ status, error }, setState] = React.useState({
    status: 'idle',
    error: null,
  });

  async function handleSubmit(event) {
    event.preventDefault();
    const newUsername = event.target.elements.username.value;
    setState({ status: 'pending' });
    try {
      await updateUsername(newUsername);
      setState({ status: 'fulfilled' });
    } catch (e) {
      setState({ status: 'rejected', error: e });
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="username">Username</label>
      <input id="username" />
      <button type="submit">Submit</button>
      <span>{status === 'pending' ? 'Saving...' : null}</span>
      <span>{status === 'rejected' ? error.message : null}</span>
    </form>
  );
}

input에 username을 입력하고 엔터를 눌러 submit을 하면 username이 등록된다. 그리고 status에 따라서 메세지가 달리 뜬다.

그럼 테스트 코드를 작성해보자

test('calls updateUsername with the new username', async () => {
  const handleUpdateUsername = jest.fn();
  const fakeUsername = 'sonicthehedgehog';

  render(<UsernameForm updateUsername={handleUpdateUsername} />);

  const usernameInput = screen.getByLabelText(/username/i);
  user.type(usernameInput, fakeUsername);
  user.click(screen.getByText(/submit/i));

  expect(handleUpdateUsername).toHaveBeenCalledWith(fakeUsername);
});

그럼 act 경고문이 뜬다.

 Warning: An update to UsernameForm inside a test was not wrapped in act(...).

  When testing, code that causes React state updates should be wrapped into act(...):

잘 생각해보자. handleUpdateUsername에 들어갈 인자만 테스트 했지 saving 텍스트가 없어질거라는 테스트는 하지 않았다.
그리고 state를 업데이트하는 것은 비동기적으로 이루어진다. 그래서 saving이 없어질때까지 기다려야한다.

그래서 , saving이 없어질때까지 기다리다는 테스트 코드를 맨 아래에 적어주면 된다.

await waitForElementToBeRemoved(()=> screen.queryByText(/saving/i) )

그러나, 사실 user,fireEvent안에 이미 act가 built-in 되어있어서 사실상 사용 할 일이 많이는 없을것이라는거 염두해두자.

참고 자료

profile
'과연 이게 최선일까?' 끊임없이 생각하기
post-custom-banner

0개의 댓글