[리액트] thunk 함수 리팩토링과 테스트

심지훈·2021년 8월 13일
0

리액트

목록 보기
2/2

개요

토이프로젝트를 하던중에 thunk함수가 패턴이 반복이 되는 걸 보고 기능을 추가하기전에 이 패턴을 함수화 시켜서 thunk함수를 만드는 함수를 만들어야겠다고 생각했다. 그리고 벨로퍼트님의 리액트 강의의 나오는 구조랑 비슷해서 참고해서 하면 할 수 있겠다는 생각이 들었다.

코드

export const addOneWord = (word) => async (dispatch) => {
  dispatch(actions.requestAction());
  try {
    const payload = await axios.post("/", word);
    dispatch(actions.successAction());
    dispatch(actions.addOneWordAction(payload));
  } catch (e) {
    dispatch(actions.errorAction(e));
  }
};

export const updateOneWord = (id, word) => async (dispatch) => {
  dispatch(actions.requestAction());
  try {
    const payload = await axios.patch(`/${id}`, word);
    dispatch(actions.successAction());
    dispatch(actions.updateOneWordAction(payload));
  } catch (e) {
    dispatch(actions.errorAction(e));
  }
};

thunk 함수들은 위와 같은 구조를 가지고 있다. 요청의 시작,성공,상태변경,에러처리를 하는 thunk 함수이다. 상태변경하는 dispatch 부분빼고는 모두 비슷하다. 세세한 인터페이스들은 통일하고 아래와 같이 thunk함수를 만드는 thunk creator를 만들었다.

export function createThunkFunction(actionFunction, asyncCallback) {
  return (...params) =>
    async (dispatch) => {
      dispatch(actions.requestAction());
      try {
        const payload = await asyncCallback(...params);
        dispatch(actions.successAction());
        dispatch(actionFunction(payload));
      } catch (e) {
        dispatch(actions.errorAction(e));
      }
    };
}

const updateOneWord = createThunkFunction(
      actions.updateOneWordAction,
      axios.patch
    );

비동기 작업에 필요한 axios와 상태변경 액션 생성함수만 받아 thunk함수를 만들었다. 파라미터는 thunk함수마다 다르게 받으므로 동적으로 받을 수 있도록 스프레드 문법을 사용했다.
(이 강의교재를 많이 참고 함. https://react.vlpt.us/redux-middleware/05-redux-thunk-with-promise.html)

테스팅

createThunkFunction으로 만든 Thunk함수가 이전의 함수와 잘 작동하는지 보장은 할 수 없다.

테스팅 할 목록
1. 요청,성공, 전달받은 액션, 에러액션등을 dispatch가 잘 호출하는가?
2. 전달받은 비동기 요청 함수 'axios'는 전달받은 파라미터로 호출되는가?

테스트 코드는 아래와 같다. (jest사용)

describe("updateOneWord", () => {
    axiosInstance.patch = jest.fn();
    const updateOneWord = createThunkFunction(
      actions.updateOneWordAction,
      axiosInstance.patch
    );
    const word = { name: "hello", meaning: "world" };
    const id = 1;

    it("request", async () => {
      axiosInstance.patch.mockResolvedValue(payload);
      await updateOneWord(id, word)(dispatch);
      expect(dispatch).toHaveBeenCalledWith(actions.requestAction());
    });
    it("axiosInstance parameter ", async () => {
      axiosInstance.patch.mockResolvedValue(payload);
      await updateOneWord(id, word)(dispatch);
      expect(axiosInstance.patch).toHaveBeenCalledWith(id, word);
    });
    it("updateOneWordAction", async () => {
      axiosInstance.patch.mockResolvedValue(payload);
      await updateOneWord(id, word)(dispatch);
      expect(dispatch).toHaveBeenCalledWith(
        actions.updateOneWordAction(payload)
      );
    });

    it("error", async () => {
      axiosInstance.patch.mockRejectedValue(error);
      await updateOneWord(id, word)(dispatch);
      expect(dispatch).toHaveBeenCalledWith(actions.errorAction(error));
    });
  });
});

dispatch가 정확한 액션을 받아 호출되는지 알아보기 위해
const dispatch = jest.fn()으로 모킹함수로 선언을 하고
aiox또한 정확한 파라미터로 호출되는지 추적하기 위해 먼저 생성한 axiosInstacne( axios와 거의 동일함)을 jest.fn()으로 모킹 해준 후 , axiosInstance가 사용 할 get,post,delete,patch 메서드들을 jest.fn()으로 다시 모킹해줌으로써 파라미터 추적을 가능하게 한다.

처음에는 jest 모킹을 잘 이해하지 못해서 jest.fn('axiosInstance)axiosInstacne자체를 모킹하려 했는데, 찾을 수 없는 모듈이라고 에러가 떼서 몇시간을 해매다보니 , jest.mock()함수는 모듈 파일속에 있는 함수들을 모킹하는 역할을 한다. jest.fn()은 한 함수만을 모킹한다.

정상적으로 모든 테스트를 잘 통과한다.

profile
유연한 개발자

0개의 댓글