정글에서 온 개발자

1/4 TIL 목과 테스트 취약성 본문

TIL

1/4 TIL 목과 테스트 취약성

dev-diver 2025. 1. 5. 00:33

목 VS 스텁

둘다 목 라이브러리로 구현한다.

은 외부로 나가는 상호작용을 모방하고 검사하는데 도움
스텁은 내부로 들어오는 상호작용을 모방하는 데 도움  (검사는 하면 안됨!)

스텁과 상호작용을 검증하면 취약한 테스트가 된다. 내부 상호작용은 최종 결과가 아니기 때문

CQS 원칙

명령 조회 분리 원칙. 모든 메서드가 명령 또는 조회 중 하나여야 한다.
CQRS는 이 원칙이 객체나 시스템 단위로 확장된 것이다.

조회는 값을 반환하는 대신 부작용이 없어야 한다.(멱등성)
명령은 부작용이 있는 대신 void를 반환해야 한다. 

은 명령을 대체한다.
스텁은 조회를 대체한다.

좋은 API

공개 API 와 식별할 수 있는 동작

공개 API (public)는 식별할 수 있는 동작과 다르다.
이상적으로는 시스템의 공개 API는 식별할 수 있는 동작과 일치해야 한다.

식별할 수 있는 동작 VS 세부 구현

식별할 수 있는 동작은 다음 둘 중 하나 이상을 함

  1. 클라이언트가 목표를 달성하는데 도움이 되는 연산을 노출 (연산은 계산을 수행하거나 부작용을 초래하거나 둘 다 하는 메서드)
  2. 클라이언트가 목표를 달성하는데 도움이 되는 상태를 노출 (상태는 시스템의 현재 상태)

구현 세부사항 유출

공개 API는 시스템에 도움이 되는 연산이나 상태만 노출해야 하는데, 도움이 안되는 연산이나 상태 (구현 세부사항)이 공개API에 들어가면 구현 세부사항이 유출된 것이다. 이는 좋지 않은 설계다.

구현 세부사항을 노출하면 불변성 위반을 가져온다.  노출된 세부사항을 누구나 접근 가능하기 때문에

좋은 판단법

구현세부사항을 유출하는지 판단하는데 유용한 규칙
"단일한 목표를 달성하고자 클래스에서 호출해야 하는 연산(공개 API)의 수가 1보다 크면 구현 세부 사항 유출 가능성 있음"

즉, 캡슐화가 잘 되지 않았다는 뜻.

육각형 아키텍처와 테스트 취약성

육각형의 각의 수는 비유일 뿐, 중요하지 않다.

세가지 관점이 중요

  1. 도메인과 서비스 계층의 영향 분리 (도메인은 비즈니스 로직, 서비스는 도메인과 외부 앱의 작업 조정)
  2. 서비스만 도메인 계층에 단방향 의존한다.  도메인 내 클래스는 서로에게만 의존
  3. 외부 앱은 도메인 계층에 직접 액세스 못함.  서비스 계층의 공통 인터페이스로만 접근

이 때 시스템 내부 통신과 시스템 간 통신이 있다.
시스템 내 통신은 구현 세부사항
외부 앱과의 통신은 식별한 수 있는 동작
     앱을 통해서만 접근할 수 있는 외부 시스템, 예를들어 DB는 구현 세부사항이다.
     그 결과의 부작용은 외부에서 확인할 수 없기 때문이다. (캡슐화)

구현 세부사항을 검증하고자 목을 사용하면 취약한 테스트가 된다!

따라서 DB는 세부사항으로서 목을 사용하면 테스트가 깨지기 쉽다. 그렇다고 목을 안 쓰자니 피드백 속도가 느려져서 좋지 않은데 이에 대한 내용은 뒤에서 다룬다!


내 생각

드디어 처음에 테스트하고 싶었던 DB에 대한 내용을 다루려고 하고 있다!

최근에 읽기와 쓰기가 성격이 달라서 따로 다뤄야 하지 않을까? 하던 차에 CQS, CQRS 원칙이 나와서 반가웠다.
특히, 요즘 공부하고 있는 C++에서는 stack.pop이 결과로 top의 값을 반환하지 않고 void만 반환하는데 이게 CQS의 원칙을 지킨 대표적인 사례같다. 이렇게 해서 서 코드를 쓰는 건 조금 불편할지 몰라도 읽을 때 좀 더 명확하게 동작을 파악할 수 있다. 또 값 확인없이 pop을 하는 코드도 쓰지 않게 강제한다.

내용이 점점 어려워지고, 아키텍처 책이랑 같이 읽으니까 머리가 아파온다. 읽는 속도를 조금 늦추더라도 소화를 잘 시키면서 나가야 할 것 같다.

참고 자료

단위 테스트, 블라디미르 코리코프