비동기 코드 테스트를 위해 Async.js
와 Async.test.js
를 만들어 보도록 하자.
fetch
해 온 posts data를 setPost로 담는다.
return
되는 부분은 아래와 같다.
state에 저장한 data 값을 li
로 렌더링해주고 있다.
먼저 Async
컴포넌트를 렌더링 해준 뒤, screen
객체의 getByRole
쿼리를 사용하여 listItem(li)
를 찾아보도록 하겠다. Fetch 성공 시에만 listItem
가 그려지기 때문이다.
하지만 받아오는 data가 여러 개이기 때문에 listItem
element 또한 한 개가 아닐 것이라는 걸 생각해 보아야 한다. 이런 경우 getByRole
보다는 getAllByRole
를 사용해야 할 것이다.
(getByRole
은 하나 이상의 element가 발견된다면 작동하지 않기 때문이다)
Role에 대한 좀 더 자세한 내용은 이 문서를 통해 확인 가능하다. 간단히 말하자면 Role은 HTML을 조금 더 의미있게 작성하기 위한 속성이라 할 수 있겠다.
확인할 요소가 정해졌다면 마지막으로 expect()
를 통해 해당 요소의 어떤 상태를 예상하는지 적어주도록 하자.
우리가 원하는 결과는 listItemElement가 빈 배열이 아닌 것이다.
만약 이 배열이 비어있다면 어떠한 listItem
도 렌더링 된 것이 아니기 때문이다.
이것이 빈 배열인지에 대한 여부를 판단하기 위해 not.toHaveLength(0)
matcher를 사용하였다.
toHaveLength()
를 사용하면 배열의 길이를 확인할 수 있기 때문이다.
위와 같이 작성하고 테스트를 돌려보면 결과는 fail이 나오게 된다.
코드를 잘 작성한 것 같은데 실패하게 된 이유는 무엇일까?
에러의 내용을 보면
위처럼 접근 가능한 listItem
이라는 role을 찾을 수 없다고 나온다. listItem
을 가져오지 못한 것이다.
이 에러를 이해하기 위해서는 getAllByRole
을 좀 더 알아볼 필요가 있다. getAllByRole
은 screen
에서 즉시 요소를 찾으려 한다.
하지만 우리는 애초에 HTTP 요청(비동기)을 보내고 있다는 것을 잊어버리면 안된다...
다시 말하자면 posts data는 즉각 받아와지는 것이 아니라는 말이다.
post data가 없는 빈 배열의 Async.js
가 먼저 렌더링 되므로 처음에는 listItem
이 존재하지 않는 것이다.
첫 번째 render cycle 뒤에 useEffect()
의 Http 요청이 전송되어 응답이 돌아오게 되며, 작성한 코드에 따라 setPost(data)
를 통해 컴포넌트가 리렌더링 될 것이다.
이때서야 비로소 listItem
이 존재하게 된다.
그렇다면 비동기 코드를 테스트 하기 위해서는 어떻게 해야 할까?
getAllByRole
이 아닌 findAllByRole
을 사용하는 것이다. findAllByRole
은 find 쿼리로 promise를 반환한다는 특징이 있다.
사실 React Testing Library는 이 과정이 성공할 때까지 screen을 여러 번 재평가 하게 돠는데, findAllByRole
의 timeout 옵션을 주어 기다리는 시간을 설정할 수 있다. (기본값은 1초)
해당 예제에서는 1초만으로도 평가 시간이 충분하기에 별다른 설정을 하지는 않았다.
findAllByRole
부분에 빨간 줄이 생기는 이유는 promise를 반환하고 있기 때문이다. expect()
를 작업하기 전에 반환 결과를 먼저 기다려달라는 아우성이다..
다행히도 테스트 코드는 비동기로 사용할 수 있다.
async
와 await
를 통해 findAllByRole
의 반환 값을 먼저 받아본 뒤 expect()
를 처리하도록 하자.
다시 테스트를 돌려보면 PASS결과를 볼수 있다. 성공적으로 테스트를 통과한 것이다.
다음 글에서 더 파헤쳐보도록 하겠다.