////
Search
Duplicate
🏰

11. 단위 테스트 안티 패턴

11장에서 다루는 내용
비공개 메소드 단위 테스트
단위 테스트를 하기 위한 비공개 메소드 노출
테스트로 유출된 도메인 지식
구체 클래스 목 처리
테스트에서 시간을 어떻게 다루는지 알아보고 비공개 메소드 단위 테스트, 코드 오염, 구채 클래스의 목 처리 등과 같은 안티 패턴을 설명하며 이를 피하는 방법도 알아본다.

1. 비공개 메소드 단위 테스트

비공개 메소드를 어떻게 테스트해야할까? 전혀 하지 말아야한다.
1.
비공개 메소드와 테스트 취약성
단위 테스트를 하고자 비공개 메소드를 노출하는 경우, 식별할 수 있는 동작만 테스트하는 것을 위반하게 된다.
비공개 메소드를 노출하면 테스트가 구현 세부 사항과 결합되고 결과적으로 리팩토링 내성이 저하된다.
비공개 메소드를 직접 테스트하는 것 대신 포괄적인 식별할 수 있는 동작으로서 간접적으로 테스트하는 것이 좋다.
2.
비공개 메소드와 불필요한 커버리지
비공개 메소드가 너무 복잡해서 식별할 수 있는 동작으로 테스트하기에 충분히 커버리지를 얻을 수 없는 경우가 있다.
식별할 수 있는 동작에 이미 합리적인 테스트 커버리지가 있다고 가정하면 다음 두 가지 문제가 발생할 수 있다.
추상화가 누락되어있다. 비공개 메소드가 너무 복잡하면 별도의 클래스로 도출해야 하는 추상화가 누락되었다는 징후다.
죽은 코드다. 테스트에서 벗어난 코드가 어디서도 사용되지 않는다면 삭제하는 것이 좋다.

2. 비공개 상태 노출

단위 테스트를 목적으로만 비공개 상태를 노출하는 것이다.
비공개 상태는 '구현 세부 사항'이기 때문에 단위 테스트에서 이것을 검증할 필요는 없다. 오히려 리팩토링 내성을 떨어뜨려 나쁜 테스트를 만들게 된다.

3. 테스트로 유출된 도메인 지식

도메인 지식이 테스트로 유출되는 것은 또 하나의 안티패턴이다.
이것은 도메인 지식이 바뀔 때 마다 코드를 수정해야하며, 이는 리팩토링 내성의 저하를 불러일으킨다.

4. 코드 오염

코드 오염이란 비즈니스에 필요없고 테스트에만 필요한 코드를 추가하는 것이다.

5. 구체 클래스를 목으로 처리하기

인터페이스를 이용해 목을 처리하는 예제를 살펴봤었다.
구체 클래스를 목으로 처리하는 것이 안티패턴인 이유는 뭘까?
일부 기능을 살리기 위해 구체 클래스를 목으로 처리한다면 이는 단일 책임 원칙을 위반하는 행위다.

6. 시간 처리하기

많은 어플리케이션에는 시간에 따라 달라지는 기능이 존재할 수 있다.
시간에 따라 달라지는 기능을 테스트하면 거짓 양성이 발생할 가능성이 크다.
따라서 이 부분을 테스트 하기 위해서는 프로덕트 코드 역시 수정되어야 하고, 테스트 코드 역시 수정되어야 한다.
명시적 의존성 형태로 시간을 주입하기
LocalDateTime.now()와 같은 형식으로 생성된 시간은 테스트 할 때 거짓 양성이 나오기 쉽다.
해당 값은 매번 바뀌는 것이고 테스트에서 예측할 수도 없기 때문이다.
따라서 이것을 해결 하기 위해서는 시간에 의존적인 클래스에게 '시간 값을 가진 객체'를 주입해주는 방법으로 처리할 수 있다.

요약

단위 테스트를 가능하게 하고자 비공개 메서드를 노출하게 되면 테스트가 세부 구현 사항에 결합되게 된다. 그 결과 리팩토링 내성이 떨어진다.
비공개 메서드가 너무 복잡해서 공개 API로 테스트를 할 수 없다면, 추상화가 누락되었다는 뜻이다.
비공개 메서드를 공개로 하지 말고, 복잡한 녀석을 별도의 클래스로 추출해서 추상화한다
비공개 상태를 단위 테스트를 위해 노출하지 마라.
테스트 코드는 제품 코드와 같은 코드가 사용되는 방식을 이용해서 검증해야한다. 이것은 또한 코드 오염에 해당되기도 한다.
테스트를 작성할 때 특정 구현을 암시하지 마라.
이것은 도메인 로직이 테스트 코드에 유출된 것이고, 리팩토링 내성을 떨어뜨린다.
코드 오염은 테스트에만 필요한 코드를 제품 코드에 포함하는 것을 의미한다.
제품 코드 내에 다양한 코드가 산재되기 때문에 유지보수 비용이 증가한다.
만약 테스트를 위한 코드가 반드시 포함되어야하면 인터페이스를 하나 추가하고, 그 인터페이스의 구현체를 생성하는 것으로 코드 오염을 최소화한다.
기능을 지키기 위해 구체 클래스를 Mock으로 처리해야 한다면, 이는 단일책임원칙(SRP)를 위반한 결과다.
Mock으로 처리한다는 것은 외부 프로세스와 통신하는 역할을 하기 때문이다.
만약 이런 문제가 있다면, 외부 프로세스 통신과 도메인 로직이 있는 클래스를 분할하라.
시간에 의존적인 테스트가 있다면, 제품 코드에서 시간값 객체를 주입받아 동작하는 형태로 리팩토링 한 후 테스트 코드를 작성한다.