글을 작성하게 된 이유
좋은 품질의 코드를 위해서 거의 필수적인 부분인 테스트코드를 작성하다 생긴 궁금증입니다
어디서부터 어디까지 테스트를 짜야할까요?
당연하지만 도메인로직에 대한 단위테스트는 작성해야 할 것 같습니다
그렇다면 입출력은 어디까지 테스트를 해야할까요? 그리고 어떻게 테스트를 작성해야 할까요?
실제 우테코 전체 질답에 올렸던 질문
위 질문이 실제로 제가 올렸던 질문인데요
해결방법 1
여기에 답변이 달렸던 내용으로는 static을 그대로 유지하는 방법이 더 좋겠다는 말이 나왔습니다. 그 이유로는 원래대로라면 state가 없기에, static으로 충분히 유지할 수 있는 클래스인데, 이를 테스트만을 위해서 전략패턴을 적용해서 상속하는 부분은 테스트만을 위해서 코드가 변경되었기에 좋지않겠다는 내용인데요
테스트만을 위해서 메서드 시그니처가 변경되면 안좋다는 그 말에 정확하게 일치되는 말이기도 합니다
여기서 추가적인 문제가 발생합니다
private static final Scanner scanner = new Scanner(System.in);
InputStream in = new ByteArrayInputStream("test".getBytes());
System.setIn(in);
이렇게 메서드마다 전부 다 변경을 했어도 처음 모킹은 제대로 작동하지만, 2번째 테스트부터는 무조건 실패하게 됩니다. Scanner 는 static이 초기화될 떄 한번만 생성되는데, 이때는 첫 테스트에서 호출한 setIn 이 적용되고, 그 다음 시도에서는 그 입력을 이어서 읽어들이려고 하기에, NoSuchLineException 이 발생하게 됩니다.
이미 종료된 입력을 또 읽어들이려고 하는 것이니까요
해결방법 2
public static List<String> inputCarName() {
Scanner scanner = new Scanner(System.in);
System.out.println(INPUT_CAR_NAME_MESSAGE);
return List.of(scanner.nextLine().split(","));
}
이를 해결하기 위해서 해봤던 방법으로 매 메서드마다 전부 scanner를 새롭게 만들어주는 방법도 있었습니다
여기서 또 생기는 문제로는, 각 메서드마다 scanner가 생성되기에 성능상 문제가 생길 수 있습니다
테스트만을 위해서 메서드가 변경된 사례이기도 하고, 성능상 문제가 발생하기도 합니다.
해결방법 3
이를 해결하기 위한 방법이 질문에 나왔던 전략패턴이기도 합니다
하지만 역시나 시그니처가 변경되고, static이 전부 제거되어야 한다는 문제가 생깁니다
다시 시작된 질문
그렇다면 어떻게 해야할까요?
일단 해결방법 2번은 좋지 않은 선택이라고 생각합니다. Scanner를 매번 만든다는 것은 사실 해결방법 3으로 적용하면서, 내부에 Scanner 객체를 캐싱하는 방식으로 사용한다면 성능상 이점이 확실하기에, 성능상 이점도 없고, 메서드 시그니처가 변경되기도 하니 그렇게 커다란 장점이 없다고 생각하는데요
Input을 무조건 테스트 해야한다면, 해결방법 3번인 전략패턴을 사용하는 것이 좋을 것 같고
대부분의 경우처럼 Input을 테스트 하지 않아도 된다면,
private static final Scanner scanner = new Scanner(System.in);
을 이용해서 사용하는 쪽이 가장 깔끔할 것 같습니다
대부분의 경우에는 input은 단순하게 입력을 받아들이는 것 뿐이니 테스트 안 해도 괜찮은 경우가 많을테니까요
'Java' 카테고리의 다른 글
Java Generic 딥 다이브 (6) | 2023.02.23 |
---|---|
Generics 시작하기 (6) | 2023.02.23 |
Object 의 toString 부터 hashCode 까지 (0) | 2023.02.19 |
Enum 사용시 메서드 오버라이딩을 주의해서 사용해야 합니다 (0) | 2023.02.17 |
Java 면접 공부를 하면서 생겼던 궁금증들 (0) | 2022.10.05 |