단위 테스트에 대한 생각

JooHeon·2021년 12월 24일
0

🖊 기존의 생각

스프링 부트 환경으로 테스트를 하려면 테스트 클래스에 스프링 부트 환경을 의미하는 어노테이션을 붙여줘야한다.

@SpringBootTest
public class OrderServiceIntegralTest {
}

그리고 나는 기능마다 Junit을 사용해 테스트를 진행해왔는데, 누군가가 나에게

이건 스프링부트 환경에서만 돌아가기 때문에 단위 테스트라고 할 수 없다. 통합 테스트다

라고 말했다. 나는 스프링 부트 환경이 아니어도 기능을 테스트할 수 있어야 단위 테스트라고 생각했고 정보를 찾다보니 Mock을 알게됐다. Mock을 이용해 주입 받아야 하는 인터페이스들을 Mock 오브젝트로 바꿀 수 있어서 예상 결과만 지정해주면 테스트가 가능하고 @SpringBootTest를 안 써도 되니 '아 이게 단위테스트구나' 생각했다.

🖊 Mock 너무 불편한데 꼭 이렇게 써야하나?

나는 Mock을 사용하게 되어 너무 좋았다. 사람들이 그렇게 강조하는 단위 테스트를 드디어 하는구나 싶었는데 작업을 하다보니 너무 불편했다.

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final JpaMemberRepository memberRepository;
    private final JpaOrderRepository orderRepository;
    private final OrderQueryRepository orderQueryRepository;
    private final JpaItemRepository itemRepository;

나는 한 서비스에 인스턴스 4개를 주입받았는데, 이걸 Mock 오브젝트로 만들어서 사용하는 모든 메서드를 한땀한땀 결과 값을 지정해줬다. 처음에는 그저 Mock을 사용해서 좋기만 했는데 작업을 하면서 여러 서비스 로직을 만들다보니 공통된 검증 기능이 있었다.

@Component
public class ValidationCheck {

    //유효한 회원인지 검증
    public Member getMember(Optional<Member> byId) {
        return byId.orElseThrow(() -> new IllegalArgumentException("없는 회원 입니다"));
    }

    //유효한 상품인지 검증
    public Item getItem(Optional<Item> byId) {
        return byId.orElseThrow(() -> new IllegalArgumentException("없는 상품 입니다"));
    }

    //유효한 주문인지 검증
    public Order getOrder(Optional<Order> byId){
        return byId.orElseThrow(() -> new IllegalArgumentException("없는 주문 입니다"));
    }

}

나는 검증 기능과 관련된 메서드들을 클래스로 빼냈고 각 서비스에 이를 주입했다.

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final JpaMemberRepository memberRepository;
    private final JpaOrderRepository orderRepository;
    private final OrderQueryRepository orderQueryRepository;
    private final JpaItemRepository itemRepository;
    private final ValidationCheck validationCheck;

기존에 주입받던 인스턴스가 4개였는데 5개로 늘어났다.
문제는 검증 메서드가 들어가는 모든 테스트 케이스를 수정해야했다.
Mock 오브젝트부터 만들어서 메서드 예상 결과값을 모두 지정해줘야 할 것을 생각하니 앞이 막막해졌다. 이렇게 하는 게 최선이고 단위 테스트는 원래 이렇게 하는 걸까? 싶어서 정보를 찾아봤다.
인프런 강의에서 박기선 강사님이 Mock에 대해 소개해준 말이 나에게 너무 와닿았다.

🤔 단위 테스트의 범위는 뭘까?

박기선 강사님이 말해주시길 단위 테스트의 범위를 보는 것은 시각이 다르다고 하신다.
누군가는 어떤 행위를 단위로 볼 수 있고 누군가는 클래스를 단위로 볼 수도 있다고 한다.
나는 @SpringBootTest는 스프링 환경에서만 동작하니 단위 테스트가 아니고 통합 테스트라고 생각했는데 어쩌면 기능 별로 테스트하니 이것도 단위테스트 일 수 있다는 생각이 들었다.

🤔 결론

위에 생각했던 고민에서 Mock을 사용하면서 해결 방법이 분명 있을텐데 이를 그저 사용을 회피하는 것이 꼭 능사가 아니라고 생각한다. 하지만 아직 만족할만한 답을 찾지 못해서 일단 박기선 강사님의 고견으로 행위를 단위 테스트로 보는 관점이 타당하다고 생각해서 Junit을 사용하는 방향을 선택했다.

0개의 댓글