본문 바로가기
SPA/React

[React] Jest로 테스트할 때 비동기 처리하기

by J4J 2023. 10. 5.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 Jest로 테스트할 때 비동기 처리하는 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

비동기 처리하는 경우

 

Jest에서 비동기 처리하는 경우는 빈번하게 발생됩니다.

 

단순한 예시를 들면, 하나의 페이지에서 화면에 보이고 싶은 데이터를 가져오기 위해 API 요청을 하게 되고 전달받은 데이터들이 화면에 올바르게 보이고 있는지 확인해 볼 때 비동기 처리가 필요합니다.

 

예시를 기반으로 간단하게 소스를 작성해보면 다음과 같습니다.

 

person 조회 API

 

import axios from 'axios';
import { useEffect, useState } from 'react';

interface Person {
  name: string;
  position: string;
}

export default function App() {
  const [persons, setPersons] = useState<Person[]>([]);

  async function apiGetPersons() {
    const res = await axios.get('http://localhost:8080/getPersons');
    if(res) {
      setPersons(res.data);
    }
  }

  useEffect(() => {
    apiGetPersons();
  }, [])

  return (
    <main>
      <div>
        <h2>API로부터 호출된 Person</h2>
      </div>

      <div>
        {persons.map((person) => (
          <div>
            <h4 data-testid='person-name-text'>{person.name}</h4>
            <p>{person.position}</p>
          </div>
        ))}
      </div>
    </main>
  )
}

 

렌더링 화면 UI

 

 

 

위의 경우에서 API가 정상적으로 호출되었을 때 전달받은 name 값이 개발자의 의도에 맞게 화면에 올바르게 보이는지 확인하고 싶은 테스트 케이스를 작성하고 싶을 수 있습니다.

 

그래서 Jest를 이용하여 테스트 케이스를 다음과 같이 작성해볼 수 있습니다.

 

import { render, screen } from '@testing-library/react';
import App from './App';

describe('app test', () => {
    test('getPersons api 호출 테스트 (none)', () => {
        render(<App />);

        const personNameTexts = screen.getAllByTestId('person-name-text');
        expect(personNameTexts.length).toEqual(4);
    })
})

 

 

 

하지만 테스트 케이스를 작성하고 실행 해보면 에러가 발생되는 것을 볼 수 있습니다.

 

일반적인 API 호출 테스트 결과

 

 

 

왜냐하면 Jest가 코드를 검증할 때 API 요청에 대한 응답을 모두 전달받은 후 검증하지 않고, 검증과 동시에 API 요청 처리가 이루어지기 때문에 API 응답을 받기 전에 검증을 마친 Jest는 값을 확인할 수 없다는 이슈를 발생시킵니다.

 

이런 경우 개발자의 의도에 맞게 테스트를 올바르게 수행할 수 있도록 적용해 볼 수 있는 여러 가지 방법들이 있습니다.

 

 

반응형

 

 

setTimeout

 

먼저 활용해볼 수 있는 것은 setTimeout 입니다.

 

원하는 페이지를 렌더링 한 뒤 강제로 일정 시간이 지나게 하여 우리가 원하는 테스트를 수행하게 하면 그동안 API 요청 처리가 이루어져 테스트가 개발자의 의도에 맞게 수행될 수 있습니다.

 

하지만 이 방법은 API 처리가 얼마나 걸리는지 명확하지 않아 효율적인 방법이 아니기에 절대 사용하면 안 된다는 의미로 작성한 것이기에 활용하는 것을 지양합니다.

 

import { render, screen } from '@testing-library/react';
import App from './App';

describe('app test', () => {
    test('getPersons api 호출 테스트 (setTimeout)', () => {
        render(<App />);

        setTimeout(() => {
            const personNameTexts = screen.getAllByTestId('person-name-text');
            expect(personNameTexts.length).toEqual(4);
        }, 2000)
    })
})

 

setTimeout 테스트 결과

 

 

 

 

waitFor

 

다음은 RTL에서 제공해 주는 waitFor을 활용하는 것입니다.

 

waitFor을 이용하여 우리가 원하는 element가 화면에 보이는지 기다리고, 기다린 이후에 의도한 테스트가 동작될 수 있도록 작성해 볼 수 있습니다.

 

import { render, screen, waitFor } from '@testing-library/react';
import App from './App';

describe('app test', () => {
    test('getPersons api 호출 테스트 (waitFor)', async () => {
        render(<App />);

        await waitFor(() => expect(screen.getAllByTestId('person-name-text')[0]).toBeInTheDocument());

        const personNameTexts = screen.getAllByTestId('person-name-text');
        expect(personNameTexts.length).toEqual(4);
    })
})

 

waitFor 테스트 결과

 

 

 

 

find

 

마지막은 find를 활용하는 것입니다.

 

주로 많은 개발자 분들이 활용하는 방식은 waitFor과 find입니다.

 

그중 저는 개인적으로 find를 사용하는 편이고, 그 이유로는 부가적으로 작성되는 코드 없이 자연스러운 코드 흐름을 작성할 수 있기 때문에 많이 활용하고 있습니다.

 

 

 

find는 get과 비슷한 역할을 수행하지만 get과 달리 await을 사용할 수 있습니다.

 

그래서 다음과 같이 코드를 작성하면 API 응답을 기다리고 이후에 원하는 테스트를 진행하게 도와줍니다.

 

import { render, screen } from '@testing-library/react';
import App from './App';

describe('app test', () => {
    test('getPersons api 호출 테스트 (find)', async () => {
        render(<App />);

        const personNameTexts = await screen.findAllByTestId('person-name-text');
        expect(personNameTexts.length).toEqual(4);
    })
})

 

find 테스트 결과

 

 

 

 

 

 

 

 

이상으로 Jest로 테스트할 때 비동기 처리하는 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글