77. 단위 테스트 (Unit Test) 자동화 (JUnit, PyTest)
⚠️ 이 문서는 소프트웨어 개발의 가장 작은 톱니바퀴인 함수(Function)나 메서드(Method) 단위의 소스코드가 개발자의 의도대로 작동하는지 0.1초 만에 기계가 쪼아보는 시험을 작성하는 과정으로, **개발자가 수동으로 브라우저를 열고 클릭하며 검사하는 '육안 테스트'를 완전히 멸망시키고, 젠킨스(Jenkins) CI 파이프라인의 심장부에서 1만 개의 로봇이 자동으로 코드를 두드려 패며 불량품의 배포를 원천 차단하는 '단위 테스트(Unit Test) 자동화'**를 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 내가 짠 함수(
add(a, b))에1과2를 넣었을 때 기계적으로3이 튀어나오는지 엑스레이를 찍는 '테스트 코드'를 짜는 것이다. 본 코드를 짜는 것만큼 귀찮지만, 나중에 버그 잡는 수백 시간을 아껴주는 적금과 같다.- 가치: 한 달 뒤 신입 사원이 코드를 잘못 건드렸을 때, 수백만 개의 단위 테스트 로봇 중 하나가 즉시 삐-! 하고 빨간불을 울려(CI 빌드 실패) 운영 서버로 치명적 버그가 침투하는 것을 자동문처럼 막아주는 궁극의 방어막(Safety Net)이다.
- 기술 체계: 자바 진영의 JUnit/Mockito, 파이썬의 PyTest 같은 프레임워크를 사용하며, 외부 DB나 API의 응답을 기다리지 않고 '가짜 객체(Mocking)'를 쑤셔 넣어 오직 해당 함수의 로직 순수성만을 0.001초 단위로 격리 검증하는 것이 철칙이다.
Ⅰ. 수동 테스트의 몰락과 안전망(Safety Net)의 구축
사람의 클릭(QA)을 믿고 배포하는 시대는 끝났다.
- 초보 개발자의 프린트(Print) 노가다:
- 장바구니 계산 로직을 짠 뒤, 초보자는 브라우저를 띄워 아이템 3개를 담고 마우스로 [결제] 버튼을 눌러보거나, 코드 중간에
System.out.println(금액)을 박아놓고 콘솔 창을 눈으로 읽으며 "아, 계산 잘 되네" 하고 배포한다. - 치명적 단점: 내일 배송비 로직을 추가하려고 코드를 슬쩍 고쳤다. 장바구니 계산이 망가졌는지 알려면 어제 했던 그 짓(클릭 노가다)을 10분 동안 똑같이 100번 다시 해야 한다. 인간은 귀찮아서 안 하고, 버그는 그대로 운영에 나간다.
- 장바구니 계산 로직을 짠 뒤, 초보자는 브라우저를 띄워 아이템 3개를 담고 마우스로 [결제] 버튼을 눌러보거나, 코드 중간에
- 단위 테스트 (Unit Test) 코딩의 마법:
test_cart_total_price()라는 별도의 테스트 코드를 작성한다.- "사과(100원)와 배(200원)를 넣고 계산 함수를 때렸을 때, 결과가
300과 정확히 일치(AssertEquals)하는지 로봇아 네가 비교해 봐라!" - 개발자가 내일 코드를 고친 뒤 터미널에
npm test단 한 줄만 치면, 컴퓨터가 0.1초 만에 테스트 코드 1만 개를 쫙 돌려서 "장바구니 함수는 통과(초록불 ✅), 배송비 함수는 에러(빨간불 ❌)"라고 100% 자동 채점을 해준다.
📢 섹션 요약 비유: 공장에서 자동차 브레이크를 조립할 때마다 직원이 직접 차를 몰고 나가 100km/h로 달려가며 벽 앞에서 밟아보는 짓(수동 테스트)은 시간 낭비에 목숨을 건 도박입니다. 단위 테스트는 공장 라인 한구석에 '로봇 브레이크 검사기(Unit Test)'를 달아두고, 부품이 조립되자마자 0.1초 만에 기계가 브레이크를 사정없이 밟아보며 100% 안전(초록불) 도장을 찍어주는 무인 품질 검사소입니다.
Ⅱ. 독립성과 Mocking (모의 객체) 기법
DB가 뻗었다고 해서, 내 함수의 테스트가 실패하면 안 된다.
- 외부 환경 의존성이라는 독(Poison):
- "고객 정보를 DB에서 가져와 VIP 등급을 매기는 함수"를 테스트한다고 치자.
- 테스트 코드가 진짜 MySQL DB에 접속해서 데이터를 퍼오면 어떻게 될까? 어느 날 DB 서버가 점검으로 꺼지면 내 함수의 로직은 아무 잘못이 없는데 테스트 코드가 뻥 터지면서 빨간불(에러)을 냅니다. 이건 제대로 된 단위 테스트가 아니다.
- 단위(Unit) 격리의 철칙:
- 진정한 단위 테스트는 네트워크, 하드디스크, 외부 API, 실제 DB 등 외부 환경과 연결된 모든 탯줄을 100% 가위로 끊어버려야 한다.
- Mocking (모의 객체 / 가짜 데이터) 투입:
- 이때 구원투수(Mockito 라이브러리 등)가 등장한다. 외부 DB 객체를 껍데기만 있는 **'가짜 로봇(Mock)'**으로 바꿔치기한다.
- 테스트 코드에 이렇게 적는다. "야 가짜 DB 로봇아! 내 함수가 너한테
getUser()라고 말을 걸면, 넌 진짜 DB 가지 말고 그냥 무조건[홍길동, 가입일 10년]이라는 가짜 텍스트를 즉시 뱉어줘(Stubbing)!" - 이렇게 하면 외부 변수를 0으로 통제한 채 오직 내 if 문(10년이면 VIP인가?) 로직 자체가 완벽한지만 0.001초 만에 고립시켜 엑스레이 검증할 수 있다.
📢 섹션 요약 비유: 권투 선수의 발차기 실력(단위 함수 로직)만 순수하게 채점하고 싶은데, 진짜 상대방(실제 DB)을 링에 올리면 상대가 도망가거나 컨디션이 나빠서 발차기 테스트 자체가 불가능해집니다. 그래서 진짜 사람 대신 허공에 매달린 '가짜 샌드백(Mock 객체)'을 매달아 둡니다. 샌드백은 무조건 제자리에 있으므로, 언제든 선수 혼자서 방해받지 않고 정확한 파워와 각도(로직의 순수성)만을 0.1초 만에 100번씩 타격하며 검증할 수 있는 완벽한 격리 훈련장입니다.
Ⅲ. CI 파이프라인의 자물쇠 (Quality Gate)
테스트 코드가 없는 코드는 젠킨스 문턱을 절대 넘을 수 없다.
- TDD (테스트 주도 개발)의 로망과 현실:
- 본 코드를 짜기 전에 테스트 코드부터 먼저 짜고(Red), 코드를 욱여넣어 통과시키고(Green), 예쁘게 다듬는(Refactor) TDD 기법이 훌륭하긴 하지만, 바쁜 SI 현실에서는 지키기 어렵다. 하지만 어떻게든 다 짜고 나서라도(Test After) 테스트 코드는 존재해야 한다.
- 젠킨스 (Jenkins) 파이프라인 연동:
- 개발자가 깃허브에 코드를 푸시(Push)한다.
- CI 서버(젠킨스)가 깨어나 빌드를 돌리기 전에 무조건
mvn test를 1번 빠따로 실행한다. - 테스트 코드 1만 개 중 단 1개라도 빨간불(Fail)이 뜨면? 젠킨스는 "결함이 발견됐다!"라며 사이렌을 울리고 파이프라인 컨베이어 벨트를 그 자리에서 꽝! 정지시켜 버린다(Build Fail). 쓰레기 코드는 도커 이미지(아티팩트)로 구워질 자격조차 박탈당한다.
- 코드 커버리지 (Code Coverage) 80% 강제:
- 똑똑한 데브옵스 팀은 소나큐브(SonarQube)나 자코코(JaCoCo) 같은 커버리지 검사기를 CI에 달아둔다.
- "개발자가 짠 100줄의 소스코드 중, 이 단위 테스트 로봇들이 몇 줄이나 검사(커버)했는가?"를 퍼센트로 잰다.
- 커버리지 80% 미만(테스트 안 짠 꼼수)이면 퀄리티 게이트(Quality Gate)를 닫아버려 아예 깃허브 메인 브랜치에 합쳐(Merge)지지도 못하게 시스템적으로 락(Lock)을 걸어버리는 지독한 통제가 이뤄진다.
📢 섹션 요약 비유: 공장 출구에 무서운 X-ray 검색대(CI 파이프라인 단위 테스트)를 세워뒀습니다. 직원이 불량품(버그 코드)을 박스에 슬쩍 밀어 넣어도 검색대를 통과하는 순간 사이렌이 울리며 셔터가 쾅 닫힙니다(배포 중단). 심지어 직원이 귀찮아서 제품의 겉면만 대충 검사하고(커버리지 20%) 넘기려 하면, 기계가 "전체의 80% 이상 구석구석 검사 도장 안 찍혔네? 넌 퇴근 금지!"라며 물건을 강제로 반송해 버리는 절대 타협 불가한 무결점 팩토리 보안 시스템입니다.