Mock의 적용 범위 #13
Replies: 3 comments
-
|
Mock은 "테스트 대상이 의존하는 객체를 가짜로 대체하는 것" 입니다. 여기서, 테스트 대상과 그 대상의 어떤 책임을 확인하고 싶은지가 중요하다고 생각합니다! mock 적용Service를 예시로,,, class XXXService {
private final XXXRepository repository;
public Reservation reserved(parameters~ ){
boolean isDuplicated = repository.existsDateIdAndTimeId();
validateDuplicatedDateTime(isDuplicated );
}
}위 상황에서 Mock을 사용하지 않을 경우,, 서비스에서 중복검증을 수행한다는 걸 어떻게 테스트 코드로 작성할 수 있을까요!? Mock을 사용하여, 어떤 책임을 확인하고 싶은지다음 예시처럼 class XXXService {
private final XXXRepository repository;
public Reservation reserved(parameters~ ){
boolean isDuplicated = repository.existsDateIdAndTimeId();
validateDuplicatedDateTime(isDuplicated );
}
}서비스의 책임이 "중복되는 날짜/시간이 있다면 예외를 발생한다" 일 때, 중요한 건, 비즈니스 요구사항에 어긋날 때, 예외를 적절히 던져주고 있냐입니다. 따라서 DB가 어떻게 동작하는 지는 서비스의 책임이 아니며, existsDateIdAndTimeId()가 true일 때, 예외를 잘 처리하느냐가 테스트 관심사인 것이죠. |
Beta Was this translation helpful? Give feedback.
-
|
Mock은 테스트 대상이 의존하는 객체를 가짜 객체로 대체하는 것이라고 생각합니다. 다만 Mock을 어디까지 적용할지는 단순히 “의존성이면 전부 Mock 처리한다”로 정하기보다, 예를 들어 Service를 테스트한다고 했을 때, 하지만 반대로 Repository의 SQL, DB 매핑, 제약 조건, 트랜잭션까지 함께 검증하고 싶다면 현재 제가 내린 결론은, 외부 시스템을 제외하고는 Mock 사용을 최대한 지양하는 편이 좋다는 것입니다. 여기서 말하는 외부 시스템은 외부 API, 결제 서버, 메일 서버, 푸시 알림, S3처럼 반면 애플리케이션 내부의 도메인 객체나 Repository까지 무조건 Mock 처리하는 것은 조심해야 한다고 느꼈습니다. 그 결과 테스트가 구현 방식에 강하게 묶이고, Mock 적용 범위를 정하는 기준Mock 적용 범위는 먼저 아래 질문에서 출발해야 한다고 생각합니다.
Service의 책임을 테스트하고 싶다면, Service가 외부 의존성의 결과를 바탕으로 올바른 판단을 하는지에 집중할 수 있습니다. 예를 들어 Service의 책임이 “특정 조건에서 예외를 던지는 것”이라면, 테스트의 관심사는 실제 Repository가 DB에서 데이터를 잘 조회하는지가 아니라, Repository가 어떤 결과를 줬을 때 Service가 올바른 판단을 하는지일 수 있습니다. 이런 경우에는 Repository를 Mock이나 Fake 같은 테스트 대역으로 대체하는 선택이 가능하다고 생각합니다. 반대로 Repository의 책임을 테스트하고 싶다면, SQL이 의도대로 실행되는지, DB 매핑이 올바른지, 제약 조건이 잘 동작하는지, 트랜잭션이 의도대로 묶이는지를 실제 DB와 함께 검증해야 할 수 있습니다. 즉, Repository를 Mock 처리할지 실제 DB를 사용할지는 “Repository는 의존성이니까 Mock”이라는 기준이 아니라, 이번 테스트에서 DB 동작까지 검증 대상에 포함할 것인가로 판단해야 한다고 생각합니다. 그래서 Mock은 “모든 의존성을 대체하기 위한 기본값”이라기보다, 테스트 관심사가 아닌 의존성을 통제하기 위한 선택지라고 생각합니다. 다만 제 기준에서는 외부 시스템을 제외하고는 Mock 사용을 최대한 보수적으로 가져가고 싶습니다. Mock을 많이 사용하면 테스트가 빠르고 고립되기는 하지만, 실제 유즈케이스의 동작보다 내부 구현의 호출 방식에 묶이기 쉬워지기 때문입니다. 외부 시스템과 DB는 어떻게 볼 수 있을까?제가 Mock 사용을 적극적으로 고려하는 대상은 외부 시스템, 특히 네트워크를 타야 하는 의존성입니다. 예를 들어 외부 API, 결제 서버, 메일 서버, 푸시 알림, S3 같은 의존성은 실제로 호출하면 테스트가 느려지고, 불안정해지고, 비용이나 부작용이 발생할 수 있습니다. 이런 경우에는 Mock이나 Fake를 사용해서 테스트 대상의 책임에만 집중하는 것이 적절하다고 생각합니다. 다만 DB는 조금 특수하다고 생각합니다. DB도 넓게 보면 외부 리소스이지만, 애플리케이션의 핵심 상태를 저장하고 비즈니스 정합성을 보장하는 중요한 협력 대상입니다. 특히 현재 저희가 진행하는 미션들처럼 Repository, SQL, 트랜잭션, 제약 조건까지 학습하고 검증해야 하는 상황에서는 실제 DB를 사용하는 테스트가 더 신뢰도 있다고 느꼈습니다. 또 한 가지 고려할 점은 그 DB에 접근하는 주체가 누구인가라고 생각합니다. 만약 해당 DB를 오직 우리 서비스만 접근한다면, 애플리케이션 코드와 DB 제약 조건을 함께 설계하고 통제할 수 있습니다. 이 경우 테스트에서도 실제 DB를 사용해 우리 서비스의 유즈케이스가 끝까지 잘 동작하는지 확인하는 의미가 큽니다. 반대로 우리 서비스가 아닌 다른 시스템도 같은 DB에 접근한다면 이야기가 달라질 수 있습니다. 다른 서비스나 배치, 레거시 시스템이 같은 데이터를 수정한다면, 우리 애플리케이션 코드만으로 정합성을 보장하기 어렵습니다. 이때는 테스트에서도 단순히 Repository를 Mock 처리하기보다, DB 제약 조건, 트랜잭션, 동시성, 데이터 상태 변화까지 더 중요하게 봐야 할 수 있다고 생각합니다. 즉, DB는 단순히 “외부 시스템이니까 Mock”으로 보기보다, 우리 서비스의 상태 저장소인지, 여러 시스템이 공유하는 저장소인지, 정합성 검증이 필요한 대상인지를 함께 고려해야 한다고 생각합니다. Mock을 과하게 사용했을 때의 문제Mock을 과하게 사용하면 테스트가 실제 유즈케이스보다 구현 방식에 강하게 묶일 수 있다고 생각합니다. 특히 Mock 기반 Service 단위 테스트는 “실제 유즈케이스가 동작하는가”보다 “내가 예상한 구현 순서대로 메서드가 호출됐는가”를 검증하는 형태가 되기 쉽습니다. 예를 들어 Repository 호출 여부나 호출 순서를 지나치게 검증하면, 테스트는 사용자가 기대하는 기능의 결과보다 내부 구현 흐름에 더 강하게 의존하게 됩니다. 이 경우 실제 기능은 동일한데 내부 구현을 리팩터링했다는 이유만으로 테스트가 깨질 수 있습니다. Repository 메서드 이름을 바꾸거나, 조회 방식을 바꾸거나, 호출 순서를 조정했을 때 사용자 관점에서는 아무 변화가 없는데 테스트가 실패할 수 있습니다. 그 결과 테스트가 구현 방식에 강하게 묶이고, 리팩터링 내성이 약해집니다. 또한 실제 객체들이 함께 동작했을 때 발생할 수 있는 문제를 놓칠 수 있어, 테스트 신뢰성도 떨어질 수 있다고 생각합니다. 그래서 저는 외부 시스템처럼 Mock의 이점이 분명한 경우가 아니라면, Mock 사용을 최대한 지양하는 편이 좋겠다고 판단했습니다. 현재 제가 생각하는 Mock 적용 기준현재 저는 Mock 적용 기준을 이렇게 정리하고 있습니다.
핵심은 Repository나 DB를 무조건 Mock 처리하지 않는 것입니다. Service의 판단 로직만 아주 좁게 검증하고 싶다면 Repository를 Mock으로 대체할 수는 있습니다. 하지만 실제 SQL, DB 매핑, 트랜잭션, 제약 조건, 동시성 상황까지 검증하고 싶다면 실제 DB를 사용하는 통합 테스트가 더 적절할 수 있습니다. 또한 Mock을 사용한 Service 단위 테스트가 실제 유즈케이스 검증보다 호출 순서 검증에 가까워지고 있다면, 그 테스트는 다시 점검해볼 필요가 있다고 생각합니다. 이번 레벨2 미션에서의 결론이번 미션에서는 Mock 사용이 제한된 상황이기도 했고, 주요 기능이 대부분 예약 생성, 조회, 변경, 삭제처럼 도메인 규칙과 DB 상태 변화에 연결되어 있었습니다. 그래서 Repository를 모두 Mock 처리한 Service 단위 테스트를 억지로 만들기보다는, 아래 방향이 더 적절하다고 생각했습니다.
결론적으로 Mock은 테스트를 빠르게 만들기 위한 기본 선택지가 아니라, 테스트 대상의 책임에 집중하기 위해 필요한 경우 선택하는 도구라고 생각합니다. 다만 현재 제 기준에서는, 외부 시스템처럼 Mock의 이점이 명확한 경우를 제외하고는 Mock 사용을 최대한 지양하는 편이 좋다고 판단했습니다. 특히 Mock 기반 Service 단위 테스트가 실제 유즈케이스의 동작이 아니라 내부 메서드 호출 방식만 검증하게 된다면, 테스트가 구현에 묶이고 리팩터링 내성이 약해지며 신뢰성도 떨어질 수 있다고 생각합니다. 따라서 Mock 적용 범위는 “모든 의존성을 Mock할 것인가?”로 정하기보다, 테스트 대상이 누구인지, 어떤 책임을 검증하고 싶은지, 그리고 실제 의존성을 포함했을 때 테스트 신뢰도가 더 높아지는지를 기준으로 결정하는 것이 좋다고 생각합니다. |
Beta Was this translation helpful? Give feedback.
-
Mock이란 무엇일까?Mock은 테스트 대상이 의존하는 객체를 흉내내는 가짜 객체입니다. Mock을 왜 사용할까?
단위 테스트 작성 시 어디까지 Mock을 적용해야 할까?먼저 생각해볼 수 있는 부분은 단위 테스트가 경계를 어디까지 설정하고 있을까에 대한 부분입니다.
그럼 우리는 단위 테스트를 어디까지 Mock을 적용해야할까? 에 대한 질문에 답을 해보자면 다음과 같이 정리할 수 있을거 같습니다. Mock 의존성의 성격으로 나눠본다.
또한, 테스트를 통해 모든 것을 검증하려고 하지 않고, 테스트 피라미드로 역할들을 분담하는 것이 필요하다고 생각합니다. 즉, 단위 테스트, Repository 테스트, 통합 테스트, 엔드투엔드 테스트까지 각 테스트에 책임을 어떻게 지고 있을까에 대한 생각으로 테스트를 작성하는게 중요하다고 생각합니다. 결론은, “Mock을 어디까지 적용해야할까?” 라는 질문에 “지금이 테스트 코드의 책임이 무엇인가?”를 생각하며 본질적인 질문으로 접근하는 자세가 필요할거 같습니다. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Mock이 무엇인가요?
단위 테스트 작성 시 어디까지 mock을 적용 해야 할까요?
Beta Was this translation helpful? Give feedback.
All reactions