단위 테스트란?
단위 테스트란 무엇일까?
당장 구글에 단위 테스트를 검색하면 '가장 작은 단위의 코드 블록(함수 또는 메서드)을 검증하는 테스트'라는 설명이 보편적이다.
나도 그렇게 알고 있었고, 실제로 그렇게 많이 알려져 있는 듯 하다.
Classicist, Mockist
그런데 최근에 단위 테스트란 무엇인가를 정의하는 두 학파(클래시스트(Classicist), 모키스트(Mockist)) 가 있다는 것을 알게 됐다. 대뜸 처음 듣는 말이어서 두 학파의 이야기를 알아보았다.
두 학파가 다르게 보는 것은 '무엇이 단위인가?' 에 대한 것이다. 여러 가지 차이가 있지만, 근본적으로 단위를 보는 관점이 다름으로부터 오는 차이들이다.
Classicist 는 단위를 [관련 클래스들의 모음] 으로 정의한다. 이는 너무 포괄적인 의미인데, 내 방식대로 이해한 것은 [유스케이스 단위] 또는 [기능]이다.
Mockist 는 단위를 [단일 클래스] 로 정의한다.
위처럼 단위를 보는 관점이 다르고, 따라서 Classicist 학파의 단위 테스트는 관련된 실제 객체를 사용하고 되도록 모킹(가짜 객체 사용)을 피하며, 외부 의존성(DB, 외부 api 등)만 가짜 객체를 사용한다. 객체 간의 상호작용보단 결과 검증, 상태 검증 중심이다.
Mockist 의 단위 테스트는 모킹 중심이다. 테스트 대상을 격리하고 의존 객체를 모킹하여 단일 클래스 검증에 집중한다. 객체 간 상호작용과 행위 검증 중심이다.
어떤 것을 단위로 봐야 하나?
요컨대 현재 보편적으로 알려진 단위 테스트의 정의는 모키스트 학파의 단위테스트인 것이다.
나는 모키스트의 정의대로 단위와 단위 테스트를 이해하고 있었으므로, 클래시스트 단위 테스트를 보면서 (무슨 소릴 하는가 보자)는 심보로 여러 아티클들을 읽어 내려갔다.
사실 대부분의 아티클들은 두 개념은 각자 틀리지 않은 접근이고, 둘은 대결하는 관계가 아니라는 이야기를 한다. 그것은 곧바로 수용할 수 있었다.
그리고 생각을 조금씩 얹어 보면서 코드와 구현의 가치가 낮아지는 ai 시대에, 상세 구현과 상호작용보다 결과에 집중하는, 즉 단위를 행동으로 보는 관점이 지금 우리에게 더 필요한 것이 아닌가 하는 생각을 하게 되었다.
요구사항과 구현이 순식간에 바뀌고 그 비용도 현저히 낮아진 지금, 구현에 밀착된 테스트가 무엇을 지켜줄 수 있을까 하는 생각이 든다.
거짓 음성
두 학파의 단위테스트는 거짓 음성에 대한 면역(?)도 다르다.
테스트는 통과하는데 운영에서 실패하는 현상을 거짓 음성(False-Negative)이라 한다.
모키스트의 단위 테스트는 극소한 범위의 단위만을 테스트한다. 모킹 중심의 테스트이기 때문에, 객체가 실제 의존하는 외부 요소들(DoC)은 대부분 가짜 객체로 이루어져 있다. 따라서 테스트 대상의 논리가 옳아 테스트에 통과하더라도, 의존 하고 있는 요소(DoC)의 문제나 변경을 테스트로 잡아내기 힘들다.
클래시스트의 단위 테스트는 하나의 유스케이스에 포함된 모듈 내 모든 요소를 실제 객체를 동원하여 테스트한다. 내부 요소 하나의 장애가 테스트 실패로 전파되는 우려가 있긴 하지만, 실제 객체 자체를 테스트하므로 모키스트 단위 테스트보단 거짓 음성에 강하다.
어차피 통합 테스트로 커버한다면 거짓 음성을 걱정할 필요 없지 않나?
모키스트 단위를 채택하든 클래시스트 단위를 채택하든, 통합 테스트라는 안전장치가 시스템의 안정성을 책임질 수 있다는 것은 사실이다. 그렇지만 실질적인 개발 과정에서, 통합 테스트만을 검증 루프로 사용할 수는 없다. 다음과 같은 두 가지 이유가 있다.
검증이 필요한 부분만 검증할 수 없다.
코드 한 줄 작성하고 통합테스트를 돌려볼 것인가?
어디서 문제가 발생했는지 추적이 어렵다.
통합 테스트가 실패했는데, 이 원인을 어디에서 찾을 것인가?
· · ·
통합 테스트가 필요한 것은 사실이나, '단위 테스트가 검증할 수 있는 것들'을 검증하기 위해 단위 테스트가 필요하다.
거짓 양성
거짓 음성의 반대 개념이 거짓 양성 (False-Positive)이다.
테스트는 실패하는데 실제로는 아무것도 잘못되지 않은 경우를 거짓 양성이라 한다. 이 성질을 뒤집어 리팩터링 내성이라 부르기도 한다.
모키스트의 단위 테스트는 여기에 구조적으로 약하다. 상호작용을 검증한다는 것은 결국 구현 방식을 검증한다는 것이기 때문이다. 예를 들어, 재고 차감 로직을 다른 객체로 옮기면 주문 결과는 그대로인데 호출을 검증하던 테스트만 깨진다.
이런 실패가 반복되면 더 큰 문제를 낳는다. 리팩터링할 때마다 테스트가 깨지면 [테스트가 깨지면 테스트를 고친다]는 습관이 생기고, 진짜 회귀가 발생했을 때도 같은 취급을 받게 된다. 즉 거짓 양성은 거짓 음성을 부른다.
이런 관점에서 모키스트의 단위 테스트는 거짓 음성과 거짓 양성 모두에 취약한 셈이다.
무엇을 택할 것인가?
앞으로는 어떤 것을 단위로 바라봐야 할까?
클래시스트 단위 테스트로 돌아가자는 메세지를 담은 듯한 글이 됐지만,
정답은 늘 그렇듯 트레이드 오프다.
ai 시대가 도래한 지금, 코드가 빠르게 만들어지고 버려지는 와중에 구현과 밀착해 있는 모키스트 방식의 테스트는 필요도가 떨어진다고 생각할 수 있다. 또 기능 중심으로 결과만 검증하여 수많은 리팩토링을 견뎌낼 수 있는 클래시스트 방식의 테스트가 더 각광받지 않을까 하는 생각도 하게 된다. 물론 구현이 빠르게 변하기 때문에, 구현을 보호하는 모키스트 테스트에 비해 의도된 행동 자체를 보호하는 클래시스트 테스트가 그런 부분에선 장점이 있다.
그러나 알림을 보냈는지, 이벤트를 발행했는지처럼 결과가 아닌 행위에 대한 검증이 필요한 케이스가 있다. 이런 상황에서는 객체간의 상호작용 자체를 검증하는 모키스트의 단위 테스트가 가치를 발휘한다. 또한 쪼개진 단위의 빠른 검증 루프와 실패 지점 식별이 가능한 점은 실제 개발 프로세스 상의 단위 테스트라는 영역에서 여전히 필요한 부분이다. 테스트 가능한 설계를 유도하는 구조도 유효한 강점 중 하나다.
중요한 것은 하나의 제품 안에서도 도메인에 따라, 레이어에 따라 단위를 보는 관점이 달라야 할 수 있으며 테스트를 수행하는 방법도 필요에 맞게 가져가야 함을 아는 것이다.
정작 ai 시대에도 중요한 것은 어떻게 단위를 바라보는 것이 정답인가를 아는 게 아닌, 이 행위를 어떤 경계로 어떻게 테스트해야 가장 저렴하고 정확하게 보호할 수 있는가를 판단하는 능력이라고 생각한다.
'Study' 카테고리의 다른 글
| 가족 아카이브 (1) | 2026.08.08 |
|---|---|
| DDD 는 silverbullet일까? (0) | 2026.08.03 |
| 코드리뷰를 왜 할까? (0) | 2026.07.29 |
| 자소서 퍼블리셔 스킬 만들기 (0) | 2026.06.07 |
| 피드백서비스 개선기 (0) | 2026.01.22 |