본문 바로가기

Programing/테스트

[Java] Enum에 없는 값을 테스트할 때는 어떻게하지?

통신사 멤버십을 관련 기능을 개발하게 되었다고 하자. (예를 들 때는 현실성이 있는 것이 좋으므로...)

아래와 같은 Enum이 있다고 하자.

public enum MembershipType {
    KT,
    SKT
}

통신사에 따라 사용하는 API가 달라서 묶어주는 구조로 되어 있다고 하자.

MembershipInspector 는 내부적으로 KtMembershipInspector와 SktMembershipInspector 를 선택적으로 호출한다.

물론 다형성을 이용한 전략 패턴을 적용기에 좋은 구조이다. 하지만 갯수가 많지 않아 switch~case로 분기하게 되어 있다고 치자.

코드는 아래와 같이 표현이 가능할 것이다.

@Service
@RequiredArgsConstructor
public class MembershipInspector {
    private final KtMembershipInspector ktMembershipInspector;
    private final SktMembershipInspector sktMembershipInspector;

    public MembershipInquiryResponse checkAvailability(@NotNull MembershipType type, @NotBlank String cardNumber) {
        requireNonNull(type, "멤버십 타입이 null입니다.");
        switch (type) {
            case KT:
                return ktMembershipInspector.checkAvailability(cardNumber);
            case SKT:
                return sktMembershipInspector.checkAvailability(cardNumber);
            default:
                // Cannot reach here
                throw new MembershipErrorException(NOT_SUPPORTED_MEMBERSHIP_TYPE, "지원하지 않는 멤버십 종류입니다.");
        }
    }
}

 

MembershipType 이 두 개지만 마지막 default를 생략할 수 없다.

왜냐하면 switch~case는 enum의 전체 원소를 확인하는 것이 아니므로 어디서인가 return이 되던지 예외가 발생하던지 되어야 한다.

테스트 커버리지

이렇게 하는 경우 default로 빠지는 경우를 테스트를 할 수 없다.

type으로 넘기는 파라미터는 KT거나 SKT거나 null 정도가 가능하다.

따라서 아래와 같이 테스트 커버리지 구멍이 된다.

MembershipErrorException 는 어떻게 테스트가 가능할까?

해결책은?

생각 외로 간단했다.

How to test unknown enum value? 라는 coderanch 의 글을 읽다가 발견한 해결책이다.

Operator는 ADD와 MINUS 뿐.

Roel De Nijs 라는 사람이 아래와 같은 답변을 했다.

곱하기(MULTIPLY)를 추가하면 되잖아.

결국 나는 LG유플러스를 추가하기로 하였다.

public enum MembershipType {
    KT,
    SKT,
    /**
     * Not use. Just for test or future implement.
     */
    LGUPLUS
}

커버리지는 100%가 되었다.

회고

왜 enum의 경우는 엘리먼트의 수를 제한 할 수 있는데 default를 두게 했을까? 없으면 뒤에서 return이나 예외를 던지던가 하게끔 했을까?

아마도 enum의 경우 미래에 추가가 된다면 switch~case의 경우 어디로 분기해야 할지 모르게 될 것이지 않을까 추측해본다.

 

아니면, 저 수준의 관점으로 가면 enum도 결국은 값비교이기 때문에 값을 벗어난 경우가 생기기 때문일 것이겠고...

나비효과

재미있는 것은...

enum의 타입의 추가가 되었는데 이후 여러 나비효과들이 발생했다.

기존 SKT는 타입이 있어서 KT인지 체크하는 메서드가 MembershipParam 라는 추상 클래스에 있었다.

추상 클래스인 이유는 여러 서브 타입에서 타입 체크를 필요로 했기 때문에 아래와 같은 구조로 되어 있었다.

'지원 가능한 타입'을 뜻하는 isSupportType로 이름이 바뀌었다.

public abstract class MembershipParam {
    protected MembershipType type;

    public boolean isKtMembership() {
        return MembershipType.KT.equals(type);
    }
}

이쪽이 좀 더 범용적인 이름인 것 같다.

public abstract class MembershipParam {
    protected MembershipType type;

    public boolean isSupportType() {
        return MembershipType.KT.equals(type) || MembershipType.SKT.equals(type);
    }
}

결국 컨트롤러에서 타입 파라미터 지우는 부분은 다시 원복!