인프런에서 종종 퇴근길 밋업을 진행하는데, 운이 좋게 원하는 주제에 참여할 수 있었다.
후기가 아닌 것 같은 후기(주저리)를 좀 작성해서 고민과 기타 등등 생각을 좀 남겨놓아야겠다.
참여 신청 계기
현재 속한 조직의 서비스를 개발하면서, 테스트 코드가 전무한 환경에서 신규 개발과 중요 비즈니스 단위의 테스트 코드나 e2e를 작성해나가고 있었다. 현재의 조직은 테스트 코드를 작성하지 않는 문화에 교류할 수 있는 개발자가 없다.
인프런의 박우빈님의 TDD강의를 입문으로, 여러 레퍼런스들을 참조하며 학습하면서 개발 과정에서 테스트 코드를 같이 작성했지만 갈수록 다음과 같은 의문이 쌓였다.
작성한 테스트 코드가 잘 짜여진 코드일까?
생산성 향상에 도움이 된다고 했는데 정말 되는걸까??
이런 의문 속에서, 한 달 전쯤 인프랩 하루님의 테스트 코드 관련 포스팅을 봤다. 그 순간 이전 데스커 라운지에서 향로님이 말씀하셨던 게 머리속을 스쳐 지나갔다.
"우리는 테스트 코드 짜면서 개발하는게 훨씬 빨라요"
혼자 머리를 부여잡고 생각해봐야 해결이 안될 것 같아, 하루님께 실례를 구하고 테스트 코드 관련 고민을 노션에 정리하여 메일을 보냈다. (이 자리를 빌려 다시 한 번 흔쾌히 받아주신 하루님께 감사인사를 전합니다. 감사합니다!!)
여튼 이러한 고민들을 하던 찰나에, 테스트 코드 밋업이 열렸다.
현재의 고민에 대해 방향성을 조금이라도 얻을 수 있다면 좋겠다는 마음에 신청했고 당첨(?)되서 다녀올 수 있게 되었다.
후기
인프랩에 재직중이신 김명일님과 김학산님께서 발표를 해주셨다.
발표자분들께서 생각하시는 잘 짜여진 테스트 코드란 어떤 것인지 듣고 현재 내가 작성하고 있는 테스트 코드와 대조해볼 수 있어서 정말 좋았다. 발표자분들이 공통으로 지향하는 방향은 모킹은 지양하며, 단위 테스트 보다는 통합 테스트의 비중을 크게 두는 방향으로 테스트 코드를 작성하시는 것 같앗다.
발표자 분들의 발표와, 질의 응답 시간을 통해 잘 짜여진 테스트 코드인가? 라는 의문은 확실하게 해소할 수 있었다.
계속해서 테스트 코드에 관심을 가지고 품질을 개선할 필요를 많이 느꼈다. 특히 불필요한 모킹을 개선하는 방향으로 계속해서 작성해보고, Fixture를 활용해 보기로 했다.
하지만, 생산성을 높일 수 있는가? 에 대한 고민은 아직 풀리지 않았다. 계속해서 테스트 코드를 작성하면서 관련 학습과 레퍼런스들을 찾아보고 생산성을 높일 수 있는 방향에 대해 해결해보고 싶다. 혹은 그런 환경에 속해서 개발을 해보고 싶다는 생각이 들었다.
종합적으로, 테스트 코드를 작성하는 개발 문화를 가진 회사들에 관심이 더 생겼던 것 같다. 특히 인프런에서 진행했던 밋업인 만큼 인프랩이라는 회사에 관심이 더 생겼고 자연스레 소속 개발자가 되어 일해보고 싶다는 생각이 들었던 시간이었다.
마지막으로 세션을 듣고 현재 작성중인 테스트 코드의 일부를 느낀대로 개선한 사례를 들며 마치도록 하겠다.
(지나가는 개발자분들이 읽고 피드백을 해주셨으면 좋겠다.. 하하)
현재 코드를 개선해보기
이 감동이 가시기전에(??) 바로 개선해보고 싶어서 밋업에서 받아들인대로 개선해보았다.
//쿠폰 다운로드 로직 중 다운 받은 쿠폰이라면 예외처리
const myCoupon = await this.myCouponRepository.findOne(
{ where:
{ no: couponNo, webId: webId }
}
);
this.isCouponDownloaded(myCoupon !== undefined);
isCouponDownloaded(couponExists: boolean): void {
if (couponExists) throw new AlreadyDownloadedCouponException();
}
it('이미 다운로드된 쿠폰은 다시 다운로드 할 수 없다.', async () => {
//given
(mockCouponService.isCouponDownloaded as jest.Mock).mockRejectedValue(
new AlreadyDownloadedCouponException(),
);
//when then
try {
await mockCouponService.isCouponDownloaded(true);
} catch (e) {
expect(e.statusCode).toBe(400);
expect(e.errorCode).toBe(200);
expect(e.message).toBe(CouponErrorMessages.ALREADY_DOWNLOADED);
} finally {
expect(mockCouponService.isCouponDownloaded).toBeCalledTimes(1);
expect(mockCouponService.isCouponDownloaded).toBeCalledWith(true);
}
});
it('이미 다운로드된 쿠폰은 다시 다운로드 할 수 없다.', async () => {
//given
const webId = 'test';
const couponNo = 1;
MyCouponFixture.create(webId, couponNo);
// when then
expect(() => MyCouponFixture.create(webId, couponNo)).toThrow(AlreadyDownloadedCouponException);
});
/#################/
class MyCouponFixture {
private static coupons: { webId: string; couponNo: number }[] = [];
static create(webId: string, couponNo: number) {
const existingCoupon = MyCouponFixture.coupons.find(coupon => coupon.couponNo === couponNo);
if (existingCoupon) throw new AlreadyDownloadedCouponException();
MyCouponFixture.coupons.push({ webId, couponNo });
return {
webId,
couponNo,
};
}
static reset() {
MyCouponFixture.coupons = [];
}
}
쿠폰이 이미 존재하는지 여부를 판단하기 위해 데이터 상태를 관리하는 MyCouponFixture를 사용했다.
실제 로직에서는 DB에 접근하여 레코드가 있다면 에러를 반환하지만, 결국 couponNo가 이미 존재한다면 에러를 뱉어주면 되기 때문에, 실제 DB에 접근하는 상황을 가정하기 위해 위해 모킹하거나 실제 테스트 환경으로 커넥션하는 형태가 불필요하다고 판단했기 때문이다.
이러한 형태로 자연스럽게 내부의 실제 단위 로직을 모킹할 필요가 없게 되었고, 테스트 코드만 보고도 어떤 테스트인지 파악할 수 있다고 느껴졌다.
픽스쳐라는 개념을 처음 접하는 것이기 때문에 이해하고 올바르게 사용하는 과정이 필요할 것 같다. 향로님의 관련 포스팅이 있으니 이걸 시작으로 픽스쳐라는 개념에 대해 학습 해나가야겠다.
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!