[React] 리액트 훅스 테스트

gak·2022년 12월 18일
0

url 과 초기값을 인자로 받아, 데이터를 패치한 뒤의 결과값과 loading Status 를 반환하는 Hooks 가 있다.

useStaleRefresh.js

import { useState, useEffect } from "react";
const CACHE = {};

export default function useStaleRefresh(url, defaultValue = []) {
  const [data, setData] = useState(defaultValue);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    // cacheID is how a cache is identified against a unique request
    const cacheID = url;
    // look in cache and set response if present
    if (CACHE[cacheID] !== undefined) {
      setData(CACHE[cacheID]);
      setLoading(false);
    } else {
      // else make sure loading set to true
      setLoading(true);
      setData(defaultValue);
    }
    // fetch new data
    fetch(url)
      .then((res) => res.json())
      .then((newData) => {
        CACHE[cacheID] = newData;
        setData(newData);
        setLoading(false);
      });
  }, [url, defaultValue]);

  return [data, isLoading];
}

useStaleRefresh 훅스의 예상 동작 플로우

  1. 맨 처음 data 에는 인자로 받은 defaultValue 값이, isLoading 에는 true 가 반환된다.

  2. 첫 랜더링이 완료 된 후 useEffect 가 실행되어, 캐시된 값이 있는지 확인한다. 처음엔 당연히 캐시된 값이 없으므로 data에는 defaultValue 값이, isLoading 에는 true 가 반환된다.

  3. API fetch가 완료 된 이후 해당 값을 캐싱한 뒤, data 에는 패치로 받은 response 를, isLoading 에는 false 가 반환된다.

  4. useStaleRefresh 에 인자로 넘겨주는 url 값이 변경될 경우, useEffect 가 실행되어 data 는 defaultValue, isLoading 은 true 를 반환한다.

  5. fetch 가 끝난 후 response 를 캐싱한다. data 는 response 값, isLoading 은 false 가 반환된다.

  6. 맨 처음 요청을 보냈던 url 을 useStaleRefresh 의 인자로 다시 넣어주면 캐싱해 두었던 값이 있으므로, data 는 캐싱해두었던 값, isLoading 은 false 를 반환한다.

  7. fetch 가 끝난 후, 갱신된 response 값을 캐싱한 후, data 는 새로운 response 값, isLoading 은 false 를 반환한다.

testing-library

훅스를 테스트 하기 위해서는 testing-library/react 의 renderHook 을(를) 이용하면 편하다.

참고) renderHook 코드
WaitFor 코드(꼭 보는걸 추천)
renderHook 요지.. -> 처음 useEffect 가 실행됨.

useStaleRefresh.test.ts


// 훅스 내부 fetch 를 모킹하기 위한 함수
function fetchMock(url, suffix = "") {
  return new Promise((resolve) =>
    setTimeout(() => {
      resolve({
        json: () =>
          Promise.resolve({
            data: url + suffix,
          }),
      });
    }, 200 + Math.random() * 300)
  );
}


describe('useStaleRefresh test', () => {
  
  beforeAll(() => {
  jest.spyOn(global, "fetch").mockImplementation(fetchMock);
  });
  
  test('useStaleRefresh 는 올바르게 작동한다.', () => {
    const defaultValue = 'defaultValue';
    
   	const { result, waitFor, rerender } = renderHook(
      () => useStaleRefresh('url1', defaultValue)
    );
    
    expect(result.current[0]).toEqual(defaultValue);
    expect(result.current[1]).toBe(true);
    
  	await waitFor(() => {
    	expect(result.current[0].data).toEqual('url1');
    });
    expect(result.current[1].data).toBe(false);
    
  
    rerender({ url : 'url2'});
    expect(result.current[0]).toEqual(defaultValue);
    expect(result.current[1]).toBe(true);
    
    await waitFor(() => {
      expect(result.current[0].data).toEqual('url2');
    });
    expect(result.current[1].data).toBe(false);
});

참고

https://www.toptal.com/react/testing-react-hooks-tutorial
https://testing-library.com/

profile
Hello. I'm Front-End Developer Trying to Create Valuable Things.

0개의 댓글