커스텀 훅에 핵심 로직이 포함되어 있었으므로 꼭 테스트 코드를 작성해둘 필요를 느꼈다.
E2E테스트로 잘 작동하는지 사용성 테스트는 완료했지만,
코드의 유지보수 측면에서 작동여부 뿐 아니라 로직 자체를 테스트할 수 있는 코드를 꼭 작성해두는 것이 좋을 것 같았다.
여기에 더해, 리팩토링의 필요성을 다시 느끼게 되었던 이유도 있었다.
그래서 이렇게 만들고 싶었다.
바로 useLocation등의 훅이 포함되어 있었기 때문에 통째로 래퍼 컴포넌트로 감싸 렌더링 한 다음, 그 안에서 커스텀 훅을 테스트해보기는 어려웠다.
따라서 mock하여 useLocation등을 구현해 함수의 로직 자체를 테스트하고, 이를 통해 케이스를 검증해가며 원하는대로 다시 리팩토링해보고자 했다.
어이구.. 조잡해라...!
어라..? 왜 테스트도 마쳤는데, 에러가 생겼을까?
테스트 인풋값은 배열만 테스트했는데, 실제로 넣은 값은 객체 형태로 안에 배열이 포함되어 있었기 때문이다.
const isChecked = property => {
return state.includes(property);
};
const isCheckedAll = () => {
return data.every(obj => state.includes(obj.name));
};
const handleCheckedAll = () => {
let updatedList = [];
if (!isCheckedAll()) {
data.forEach(obj => {
updatedList.push(obj.name);
});
}
setState(updatedList);
};
return { isChecked, isCheckedAll, handleCheckedAll };
}
/**
* @jest-environment jsdom
*/
import { TYPE_DATA } from '../constants';
describe('체크박스 테스트', () => {
let handleCheckedAll;
let isCheckedAll;
let isChecked;
let state;
let stateKey;
let stateValue;
let data;
beforeEach(() => {
state = {
category: ['guesthouse'],
};
data = TYPE_DATA.category;
stateKey = Object.keys(state)[0];
stateValue = state[stateKey];
isChecked = property => {
return stateValue.includes(property);
};
isCheckedAll = () => {
return data.every(obj => stateValue.includes(obj.name));
};
handleCheckedAll = () => {
let updatedList = [];
if (!isCheckedAll()) {
data.forEach(obj => {
updatedList.push(obj.name);
});
}
const updatedState = {
[stateKey]: updatedList,
};
return updatedState;
};
});
it('isChecked(): 체크 여부 테스트', () => {
state = {
category: ['guesthouse'],
};
expect(isChecked('guesthouse')).toBe(true);
});
it('isCheckedAll: 모든 항목 체크 여부 테스트', () => {
state = {
category: ['guesthouse', 'hotel'],
};
stateValue = state[stateKey];
expect(isCheckedAll()).toEqual(true);
});
it('handleCheckAll(): 전체 선택 기능 테스트 ', () => {
state = {
category: ['guesthouse'],
};
expect(handleCheckedAll()).toEqual({
category: ['guesthouse', 'hotel'],
});
});
});
function useCheckBox(state, setState, data) {
const stateKey = Object.keys(state)[0];
const stateValue = state[stateKey];
const isChecked = property => {
return stateValue.includes(property);
};
const isCheckedAll = () => {
return data.every(obj => stateValue.includes(obj.name));
};
const handleCheckedAll = () => {
let updatedList = [];
if (!isCheckedAll()) {
data.forEach(obj => {
updatedList.push(obj.name);
});
}
const updatedState = {
[stateKey]: updatedList,
};
setState(updatedState);
};
return { isChecked, isCheckedAll, handleCheckedAll };
}
export default useCheckBox;
string값으로 입력하고 싶지 않았다.
const handleChange = e => {
const { name } = e.target;
updateState('category', name);
};
/**
* @jest-environment jsdom
*/
describe('객체와 배열 상태 변경 테스트', () => {
let updateState;
let testObj;
let testObjWithArr;
let state;
let stateKey;
beforeEach(() => {
state = {
category: ['guesthouse', 'hotel'],
};
stateKey = Object.keys(state)[0];
updateState = (value, key = stateKey) => {
if (Array.isArray(state[key])) {
let updatedArray = [];
let updatedState = [];
if (!state[key].includes(value)) {
updatedArray = [...state[key], value];
} else {
updatedArray = [...state[key]].filter(el => {
return el !== value;
});
}
updatedState = {
[key]: updatedArray,
};
return updatedState;
} else {
let updatedState = {};
updatedState = {
...state,
[key]: value,
};
return updatedState;
}
};
testObj = {
checkin: '2022-03-22',
checkout: '2022-03-26',
};
testObjWithArr = {
category: ['guesthouse'],
};
});
it('state가 객체일 떄 전달받은 key와 value로 상태변경', () => {
state = testObj;
expect(updateState('2022-03-24', 'checkin')).toEqual({
checkin: '2022-03-24',
checkout: '2022-03-26',
});
});
it('변경할 state가 배열일 때 중복되는 값이 없으면 전달받은 value 추가', () => {
state = testObjWithArr;
stateKey = Object.keys(state)[0];
expect(updateState('hotel')).toEqual({
category: ['guesthouse', 'hotel'],
});
});
it('변경할 state가 배열일 때 중복되는 값이 있으면 전달받은 value 삭제', () => {
state = testObjWithArr;
stateKey = Object.keys(state)[0];
expect(updateState('guesthouse')).toEqual({
category: [],
});
});
});
function useUpdateState(state, setState) {
const stateKey = Object.keys(state)[0];
const updateState = (value, key = stateKey) => {
if (Array.isArray(state[key])) {
let updatedArray = [];
let updatedState = [];
if (!state[key].includes(value)) {
updatedArray = [...state[key], value];
} else {
updatedArray = [...state[key]].filter(el => {
return el !== value;
});
}
updatedState = {
[key]: updatedArray,
};
setState(updatedState);
} else {
let updatedState = {};
updatedState = {
...state,
[key]: value,
};
setState(updatedState);
}
};
return { updateState };
}
export default useUpdateState;
mock 함수로 만들다 보니 테스트를 통과하고 나면 다시 자바스크립트 파일을 수정해야 했다. 만약 커스텀 훅 자체를 가져와 테스트할 수 있다면 더 좋을텐데...