프로세스 외부 의존성을 가진 데이터베이스는 무조건 목(Mock)으로 대체해야 하는가?
데이터베이스에 값을 저장하고 로드하는 건 스텁(Stub)이나 목(Mock)으로 대체해야 할까? 그냥 직접 사용해도 되는 걸까? 달빛조각사 게임에서는 데이터베이스에 목을 사용하지 않고 테스트를 했다. 통합 테스트(Integration testing) 레벨로 테스트를 짰다. 그러면 통합 테스트에서는 데이터베이스를 그대로 사용하고 단위 테스트(Unit Testing)에는 목으로 대체해야 하는 걸까?
데이터베이스와 같은 의존성을 프로세스 외부 의존성(Out-of-process dependency)이라고 한다. ’단위 테스트 (블라디미르 코리코프, 2021)’ 책에서 프로세스 외부 의존성 테스트 가이드가 좋아서 정리해 봤다.
식별할 수 있는 동작(Observable behavior)이 아니면 목(Mock)을 사용하지 않는다
프로세스 외부 의존성이라고 무조건 목을 사용하는 건 아니다. 식별할 수 있는 동작인지 아닌지가 목 사용 여부를 결정할 좋은 기준이 된다.
프로세스 외부 의존성과의 통신은 외부에서 관찰할 수 없으면 구현 세부 사항이다. 리팩터링 후에 그대로 유지할 필요가 없으므로 목(mock)으로 검증해서는 안 된다.
좋은 예로는 애플리케이션 데이터베이스가 있다. 애플리케이션에서만 사용되는 데이터베이스다. 어떤 외부 시스템도 이 데이터베이스에 접근할 수 없다. 따라서 기존 기능을 손상시키지 않는 한 시스템과 애플리케이션 데이터베이스 간의 통신 패턴을 원하는 대로 수정할 수 있다. 해당 데이터베이스는 클라이언트의 시야에서 완전히 숨어있기 때문에 전혀 다른 저장 방식으로 대체할 수 있고, 그렇게 해도 아무도 모를 것이다.
완전히 통제권을 가진 프로세스 외부 의존성에 목(Mock)을 사용하면 깨지기 쉬운 테스트로 이어진다. 데이터베이스에서 테이블을 분할하거나 저장 프로시저에서 매개변수 타입을 변경할 때마다 테스트가 빨간색이 되는 것을 아무도 원하지 않는다. 데이터베이스와 애플리케이션은 하나의 시스템으로 취급해야 한다.
외부 의존성이냐 내부 의존성이냐는 목 사용을 결정할 때, 중요하지 않다. 식별할 수 있는 동작(Observable behavior)인지가 중요하다.
데이터베이스를 다시 설명하면 대부분 목을 사용할 필요가 없다. 애플리케이션에서 사용하는 데이터베이스를 외부 시스템이 바로 접근하게 열어두는 경우는 거의 없다. 즉, 데이터베이스는 프로세스 외부 의존성이지만 구현 세부 사항(Implementation details)에 해당한다. 그래서 목으로 검증하면 안 된다.
프로세스 외부 의존성의 두 가지 유형
- 관리 의존성(Managed dependency): 이러한 의존성은 애플리케이션을 통해서만 접근할 수 있으며, 해당 의존성과의 상호 작용은 외부 환경에서 볼 수 없다. 대표적인 예로 데이터베이스가 있다. 외부 시스템은 보통 데이터베이스에 직접 접근하지 않고 애플리케이션에서 제공하는 API를 통해 접근한다.
- 비관리 의존성(Unmanaged dependency): 해당 의존성과의 상호 작용을 외부에서 볼 수 있다. 예를 들어 SMTP 서버와 메시지 버스 등이 있다. 둘 다 다른 애플리케이션에서 볼 수 있는 부수효과를 발생시킨다.
관리 의존성은 구현 세부 사항이고 비관리 의존성은 식별할 수 있는 동작이다. 같은 말이라고 생각하면 된다.
마치며
프로세스 외부 의존성이라고 무조건 목을 사용하는 건 아니다. 프로세스 외부 의존성이 식별할 수 있는 동작이라면 목으로 대체해야 한다. 다른 테스트에 부수효과(Side effect)를 일으킬 수 있기 때문이다. 식별할 수 있는 동작이 아닌 구현 세부 사항(Implementation details)이라면 프로세스 외부 의존성을 테스트에 그대로 사용한다.
잘 모르겠다면 일단 목을 사용하지 않는 걸 추천한다. 다른 원인 때문에 테스트가 깨지는 걸 발견하면 교훈을 얻으며 그때 목으로 검증해도 늦지 않다.
링크
- NDC22 달빛조각사에서 서버 테스트 코드를 작성하는 방법 - 발표 자료, 후기 - ohyecloudy’s pnotes - ohyeclou…
- 식별할 수 있는 동작(Observable behavior) - 테스트 대상 코드를 구분하는 기준 - ohyecloudy’s pnotes -…
- 테스트 대역(Test Double)로 사용하는 목(Mock)과 스텁(Stub)은 뭐가 다른가 - ohyecloudy’s pnotes - o…
- 단위 테스트(Unit Testing)에서 단위의 경계는 무엇인가? (feat. 고전파와 런던파) - ohyecloudy’s pnotes -…
- Integration testing - en.wikipedia.org
- 단위 테스트 (블라디미르 코리코프, 2021) 독후감 - ohyecloudy’s pnotes - ohyecloudy.com