개념
테스트 용이성(Testability)이란
같은 기능을 구현해도, 코드가 어떻게 짜여있느냐에 따라 테스트하기 쉬울 수도, 거의 불가능에 가까울 수도 있다. 이 "테스트하기 쉬운 정도"를 테스트 용이성이라 부른다. 이건 코드를 짜는 개발자의 영역처럼 보이지만, QA가 이 개념을 알면 **"이 기능은 왜 테스트하기가 유독 어려운가"**를 구체적으로 짚어 개발자와 대화할 수 있다.
테스트 용이성을 낮추는 대표적인 패턴
1. 외부 의존성이 코드 안에 고정되어 있다
function sendWelcomeEmail(user) {
RealEmailService.send(user.email, "환영합니다") // 실제 이메일 서비스가 코드에 박혀 있음
}이 함수를 테스트하려면 매번 진짜 이메일이 발송된다. 테스트할 때마다 실제 이메일 서비스를 흉내 낸 가짜(모듈 7 레슨 2의 목 서버와 같은 개념)로 바꿔 끼울 수 없는 구조라면, 테스트 용이성이 낮다.
2. 함수가 너무 많은 일을 한꺼번에 한다
한 함수가 데이터를 조회하고, 계산하고, 화면에 표시하고, 로그까지 남긴다면, "계산 로직만" 따로 떼어 테스트할 방법이 없다 — 전체를 한꺼번에 실행해야만 확인할 수 있다.
3. 결과가 예측 불가능한 값에 의존한다
현재 시각, 랜덤 값, 외부 API 응답에 직접 의존하는 코드는 "같은 입력에 항상 같은 결과"가 보장되지 않아 테스트하기 어렵다(모듈 7 레슨 3에서 배운 플래키 테스트와 연결되는 지점이다).
테스트 용이성이 높은 구조
function buildWelcomeMessage(userName) {
return `${userName}님, 환영합니다`
}
function sendWelcomeEmail(user, emailService) {
const message = buildWelcomeMessage(user.name)
emailService.send(user.email, message)
}메시지를 만드는 로직(buildWelcomeMessage)이 이메일 발송과
분리되어 있고, 어떤 이메일 서비스를 쓸지도 바깥에서 주입받는다
(emailService). 이러면 테스트할 때 진짜 이메일 서비스 대신 가짜를
넣어, 실제 이메일을 보내지 않고도 "메시지가 올바르게 만들어지는지"만
따로 확인할 수 있다.
BDD/TDD와 테스트 용이성의 관계
모듈 7(레슨 5)에서 배운 TDD는 테스트를 먼저 쓰기 때문에, 자연스럽게 "테스트하기 쉬운 구조"로 코드가 설계되는 효과가 있다 — 테스트가 안 짜지는 구조는 애초에 만들 수가 없기 때문이다. 반대로 테스트를 전혀 고려하지 않고 짠 코드는 나중에 테스트를 추가하려 할 때 이 레슨에서 본 문제들(고정된 의존성, 거대한 함수)에 자주 부딪힌다.
이 모듈 전체를 종합하기
이 마지막 레슨은 모듈 9의 모든 레슨을 하나로 묶는다 — 코드를
읽고(레슨 1), 그 코드가 HTTP·인증·DB·네트워크(레슨 26)와 어떻게
연결되는지 이해하고, PR diff에서 무엇이 바뀌었는지 읽어(레슨 78),
마지막으로 그 코드가 테스트하기 쉬운 구조인지 판단하는 것까지가
"개발 지식을 갖춘 QA"가 갖춰야 할 한 세트의 능력이다. 이 능력은
스테이지 6에서 다룰 AI가 만든 코드를 검증하는 데도 그대로 쓰인다 —
AI가 짠 코드도 똑같이 "테스트하기 쉬운 구조인가"를 판단할 수 있어야
그 코드를 신뢰할 근거가 생긴다.
실무에서 왜 필요한가
"이 기능은 왜 이렇게 테스트하기 어렵죠?"라는 질문을 구체적으로 할 수 있는 QA는, 개발자에게 "외부 의존성을 분리해주시면 자동화 테스트를 훨씬 안정적으로 짤 수 있을 것 같습니다"처럼 건설적인 제안을 할 수 있다. 이건 단순히 버그를 찾는 것을 넘어, 애초에 테스트하기 좋은 시스템을 함께 만들어가는 QA로 역할이 확장되는 지점이다 — 스테이지 4(자동화)에서 이 역량이 본격적으로 쓰인다.
실습 과제
과제 1 — 테스트 용이성 낮은 코드 찾기 (15분)
다음 코드에서 테스트 용이성을 낮추는 요소를 찾아 설명한다.
function calculateShippingFee(cartTotal) {
const today = new Date()
if (today.getMonth() === 11) { // 12월은 무료배송 이벤트
return 0
}
if (cartTotal >= 50000) {
return 0
}
return 3000
}과제 2 — 개선 방향 제안하기 (10분)
과제 1의 코드를 테스트하기 쉬운 구조로 바꾸려면 어떻게 고치면 좋을지(힌트: 현재 날짜를 바깥에서 주입받게 만드는 방법) 방향을 제안한다.
자가 체크리스트
- 테스트 용이성을 낮추는 세 가지 대표 패턴을 설명할 수 있다
- 외부 의존성을 "주입받는" 구조가 왜 테스트하기 쉬운지 설명할 수 있다
- TDD가 자연스럽게 테스트 용이성 높은 코드를 만드는 이유를 설명할 수 있다
- 이 모듈에서 배운 지식들이 어떻게 하나로 연결되는지 설명할 수 있다
흔한 실수
- 테스트 용이성을 개발자만의 문제로 여기고 QA는 신경 쓰지 않는다. 테스트하기 어려운 구조를 구체적으로 짚어내는 것도 QA의 중요한 기여다.
- 테스트가 어려운 이유를 "복잡해서"라고만 뭉뚱그린다. 외부 의존성 고정, 거대한 함수, 예측 불가능한 값 의존처럼 구체적인 패턴으로 짚어야 개발자와 건설적으로 대화할 수 있다.
- 현재 시각이나 랜덤 값에 의존하는 코드의 테스트 불안정성을 당연하게 받아들인다. 이런 코드는 플래키 테스트(모듈 7 레슨 3)의 흔한 원인이며, 개선을 제안할 만한 지점이다.
참고 자료
- Michael Feathers, Working Effectively with Legacy Code — 테스트하기 어려운 코드를 다루는 기법을 정리한 고전
- Martin Fowler — Testability 관련 아티클 — 테스트 용이성과 소프트웨어 설계의 관계