TypeError: Cannot read properties of undefined (reading 'map')
const worldcup_choices = worldcup.Worldcup_choices.map((choice) => ({
choice_name: choice.choice_name,
choice_url: choice.choice_url,
}));
1) 먼저, map()함수를 인식하지 못하기 때문에 getOne()를 mock function으로 정의해주는 과정에서 map을 추가하였다.
mockWorldcupRepository.getOne = jest
.fn()
.mockImplementation((worldcup_id) => {
const Worldcup_choices = choices.map((choice) => ({
choice_name: choice.choice_name,
choice_url: choice.choice_url,
}));
return { ...getOneFuncReturnValue, Worldcup_choices };
});
2) 리팩토링을 시도해보았다. 위의 에러에서 map()이 실행되지 않았던 이유는 원래 정의된 함수의 worldcup.Worldcup_choices.map()
에서 worldcup.Worldcup_choices가 undefined가 나오기 때문에 map()을 돌릴 수 없었던 것이었다. 따라서, getOneFuncReturnValue라는 객체의 이름을 worldcup으로 변경해주고, mock getOne()에서 Worldcup_choices key만 전달을 해주었다.
mockWorldcupRepository.getOne = jest.fn(() => ({
...worldcup,
Worldcup_choices: choices,
}));
3) 실제 map()을 돌면서 map() 안에서 findAllWorldcupChoices()라는 함수를 실행하는 getAllWorldcups method에서는 실제 map()을 mockImplementation()을 통해 정의해주었다.
mockWorldcupChoicesRepository.findAllWorldcupChoices = jest.fn()
.mockImplementation(() => choices.map((choice) => choice));
const { value, error } = createCommentSchema.validate(req.body);
// validation 에러 처리, 에러 없는 경우: error === undefined
if (error) {
error.errorCode = 412;
next(error, req, res, error.message);
}
동기 함수인 validate()은 validation을 통과한 body 데이터를 첫번째 인자로 반환하고, 실패시 두번째 인자로 error를 반환한다. value는 객체로 구성되어 있어 각 값을 key-value로 저장한다.
여러개의 함수가 정의되어 있는 service layer의 비지니스 로직을 테스트하기가 쉽지 않았다. map()과 같은 내장 함수 처리에서 시간이 많이 걸렸고, 처음엔 mockRespository.getOne()과 같은 사용자 정의 함수를 mock function으로 어떻게 정의하는지도 헷갈렸었는데 지금은 확실히 이해하고 테스트 코드를 짤 수 있게 되었다.
내가 만들었던 create API에서 body 데이터에 대한 여러 validation을 주었는데 (choice_url은 무조건 uri 형태이어야함, choices 배열에는 2개 이상의 객체가 들어가야함, choices 배열 내의 객체는 짝수개여야함 등등) 이 부분을 프론트 분들께 전달해주지 않아, 계속 에러가 떠서 헤매셨다고 한다. API 문서를 꼼꼼하게 작성해야하고 클라이언트와 직접 소통하는 부분은 프론트 분들께 잘 전달해야한다는 것을 느꼈다.
에러 로깅이 매우매우매우매우 중요하다는 걸 다시금 느꼈고, 팀원들이 같이 확인할 수 있는 툴이 있으면 좋겠다고 생각했다. 배포된 서버에서 난 에러들을 다른 팀원의 터미널 pm2에서 로그 확인을 하였는데 불편한 부분이 있었다. 이 부분이 문서화가 깔끔하게 되면 좋을 것 같고, Sentry 같은 툴의 필요성을 느꼈다.
const Sentry = require("@sentry/node");
module.exports = (error, req, res, defaultMessage) => {
console.error(
`에러로그 ${error.errorCode} ${req.method} ${req.originalUrl} : ${error.message}`
);
return Sentry.Handlers.errorHandler({
shouldHandleError(error) {
if (
error.errorCode === 403 ||
error.errorCode === 404 ||
error.errorCode === 500
) {
return true;
}
return false;
}, // 1. Sentry에 error event를 저장한다.
})(error, req, res, () => {
if (!error.errorCode) {
return res.status(500).json({ errorMessage: defaultMessage });
} else {
return res.status(error.errorCode).json({ errorMessage: error.message });
} // 2. error message를 body에 전달해준다.
});
};
우리 PR merge 해야되는데...