아래의 블로그를 참고하였습니다 !
https://decembersoft.com/posts/how-to-unit-test-redux-thunks/
토이 프로젝트를 하면서 유닛테스팅을 같이 연습해보기로 했다.
리덕스 공식문서를 보면 Redux ToolKit(RTK)이란 라이브러리를 사용해서 테스팅을 설명하고 있다. RTK를 사용 할 준비가 안되었고, 배울려고 이리저리 보니 ..테스팅때문에 이미 멘탈이 나가 있어서 눈에 들어오지 않아 기존에 내가 하는 방식과 우선 비슷한 방식으로 테스팅 방법을 찾아보기로 했다.
const apiUrl = "http:/localhost:3001/word";
export const getWords = () => async (dispatch) => {
dispatch(getWordsAction());
try {
const res = await axios.get(apiUrl);
dispatch(getWordsSuccessAction(res.data));
} catch (error) {
dispatch(getWordsErrorAction(error));
}
};
위는 간단한 형식의 썽크함수이다.
가장 먼저 테스트는 DB연결, API호출, 비동기 함수등과 같이 외부 모듈에 의존적이어서는 안된다. 그래서 가장먼저
의존성을 찾는것이다. 해당 썽크 함수에서는 비동기 API호출로 axios
를 사용하고 있다. axios
가 외부에 의존적인 모듈이라고 할 수 있겠다.
axios
는 REST API호출에 주로 쓰이고 promise
를 반환하는 함수이다. axios
의 결과에 따라 썽크함수의 내부 상태가 성공( Resolve), 실패(Rejected)로 나뉜다. 따라서 테스트 역시
호출(일반적으로 API호출 상태. Pending), 성공, 실패로 나누면 된다.
describe("getWords Thunk함수 테스트", () => {
it("getWords Thunks함수를 호출합니다.", () => {});
describe("getWords Thunk함수가 성공 했을떄 ", () => {});
describe("getWords Thunk함수가 실패 했을때 했을떄 ", () => {});
});
저는 해당 블로그와 살짝 다르게 제가 생각하기에 가장 빠르고 쉽고 저에게 익숙한 일단 방식으로 했습니다. 구글링을 통해 axios
을 mocking하는 방식을 찾았고 jest.mock
을 통해 axios
를 손쉽게 mocking 할 수 있었습니다.
jest.mock("axios");
을 통해 axios 모듈을 mocking 했습니다. 그리고 axios
모듈이 반환하는 성공과 실패 값에 대한 값을 mocking합니다.
describe("getWords Thunk함수 테스트", () => {
it("getWords Thunks함수를 호출합니다.", () => {});
describe("getWords Thunk함수가 성공 했을떄 ", () => {
const res = {
data: [
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
],
};
beforeEach(() => {
axios.get.mockResolvedValue(res);
});
});
describe("getWords Thunk함수가 실패 했을때 했을떄 ", () => {
const error = new Error("My Error");
beforeEach(() => {
axios.get.mockRejectedValue(error);
});
});
});
axios
함수를 mocking 해줬으므로 각 테스트를 돌리기전에 axios
가 반환해야 할 값을 미리 정해줍니다.
axios
는 데이터를 get해옵니다. 그 형태는 json값을 예상합니다. axios.get.mockResolvedValue
을 통해 성공했을때 반환값을 mocking 합니다.axios.get.mockRejectedValue
를 통해 실패했을때 반환값을 mocking합니다.describe("getWords thunk test", () => {
describe("dispatch getWords", () => {
beforeEach(() => {
const data = [
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
];
axios.get.mockResolvedValue(data);
});
it("dispatch getWords actions", async () => {
const dispatch = jest.fn();
await getWords()(dispatch);
expect(dispatch).toHaveBeenCalledWith(getWordsAction());
});
});
describe("dispatch getWords success", () => {
const res = {
data: [
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
{
word: "Korea",
meaning: "Seoul",
},
],
};
beforeEach(() => {
axios.get.mockResolvedValue(res);
});
it("dispatch getWords actions", async () => {
const dispatch = jest.fn();
await getWords()(dispatch);
expect(dispatch).toHaveBeenLastCalledWith(
getWordsSuccessAction(res.data)
);
});
});
describe("dispatch getWords fail", () => {
const error = new Error("My Error");
beforeEach(() => {
axios.get.mockRejectedValue(error);
});
it("dispatch getWords actions", async () => {
const dispatch = jest.fn();
await getWords()(dispatch);
expect(dispatch).toHaveBeenLastCalledWith(getWordsErrorAction(error));
});
});
});
Thunk함수안에서 dispatch 될때 어떤 액션들이 제대로 호출되는지 확인 할 수 있습니다.
어떤 인자와 함께 dispatch 되는지 알아야하므로
dispatch = jest.fn()
을 통해 dispatch 함수를 mocking함수로 바꿔줍니다.
jest.fn()
의 원리는 https://www.daleseo.com/jest-fn-spy-on/ 해당 블로그를 많이 참조했습니다.
dispatch를 항상 감시하므로 성공,실패시에 정확한 액션들과 호출됬는지 테스팅합니다.
Thunk함수는 정상적으로 잘 작동 할것이라고 예상 할 수 있습니다.