[독서] 엘레강트 오브젝트 2장. 학습(2)

wally·2022년 6월 15일
0

독서시리즈

목록 보기
3/10

2.7 문서를 작성하는 대신 테스트를 만드세요

  • 이상적인 코드는 스스로를 설명하기 때문에 어떤 추가 문서도 필요하지 않다.
  • 코드 자체만으로 의미가 명확하게 전달된다.
  • 나쁜 설계가 문서를 작성하도록 강요한다.
  • 추가로 저자는 단위테스트 도한 클래스의 일부로 취급해야 한다고 한다.
  • 하지만 대부분의 언어가 불가능하기 때문에 CashTest 와 같이 테스트 네이밍을 하지만 이상적이지 않다고 주장한다.
  • 단위 테스트를 잘 작성한다면 문서화 내용을 담을 수 이싿.
  • 테스트 코드에도 관심을 기울이지

    주석도 쓰지 마라
    누구나 코드를 보고 이해할 수 있도록 코드를 작성하라

2.8 모의 객체(Mock) 대신 페이크 객체(Fake)를 사용하라.

1. 모의객체

  • Mock : 모의객체를 만들어서 사용. 그 모의 객체의 입력값과 그에 따른 결과값을 작성하고 이를 통해 기대하는 결과가 나온다는것을 보여준다
Exchange exchange = Mockto.mock(Exchange.class);
Mockito.doReturn(1.1.5)
	.when(exchange)
    .rate("USD","EUR");

문제점

  • CashExchage.rate() 를 호출한다고 가정한다.
  • Cash 클래스 내부에서 실제로 Exchange.rate() 가 호출되는지 알 수 없다. 어떻게 구현되었고 어떻게 사용되었는지 알 수 없다.
  • 불확실한 가정을 세우고 이가정을 중심으로 전체 코스트를 구축하여 사실로 바꾸어 버린다.
  • 그 외에도 이러한 단위 테스트는 너무 쉽게 깨진다. 예를 들어 인자 개수가 1개일때 새로운 rate 메서드를 사용한다고 코드 수정시 실제 동작은 문제없지만 단위 테스트는 실패한다.

    모의 객체는 블랙박스를 가지고 가정을 사실로 바꾸기에 너무 쉽게 깨지고 불안정하다. 따라서 페이크 객체를 쓰라고 추천한다.

2. 페이크 객체

import java.util.ArrayList;
import java.util.List;

public interface Exchange {
    float rate(String target);
    float rate(String origin, String target);

    final class Fake implements Exchange{
        @Override
        public float rate(String target) {
            return this.rate("USD", target);
        }

        @Override
        public float rate(String origin, String target) {
            return 1.2345f;
        }
    }
    
    default List<Float> fakeTest(String origin, String target){
        List<Float> result = new ArrayList<>();
        result.add(this.rate(target));
        result.add(this.rate(origin, target));
        return result;
    }
}
  • 페이크 객체를 쓰면 단위테스트가 쉽게 깨지지 않는다.
  • 유지보수성에서 이점이 있다.
  • 디폴트 메서드를 쓸수 있지 않을까?
    • 실제 작성해보니 리턴값이 다르고 입력매개변수가 많아지면 오히려 이해하기 어려워 질것이다. 많은 테스트를 한 메서드에 담을려하다보니 책임할당에 실패하였다. 페이크 객체가 더 나은거 같다.
  • 하지만 인터페이스는 타입이며 형틀과 같은 책임이 있는데 페이크 객체까지 있는것은 순수성을 해치는것이 아닐까?
    • 저자는 단위 테스트 역시 클래스의 일부로 취급해야 한다고 주장한다.
    • 개인적으로 인터페이스 그자체의 목적에 집중하고 싶다면 쓰지 않을 거 같다.
    • 추가로 로직이 추가되면 그때마다 페이크 객체를 수정해야 하는데 결국 유지보수성이 똑같이 좋지 않을까?
      • 하지만 그럼에도 단위테스트가 쉽게 깨지지 않고 유지보수에 도움이 되므로 지금까지 챕터중에서 가장 공감이 가고 한번 해보고 싶다는 마음도 든다.

모의 객체 대신 페이크 객체를 쓰자. 굉장히 새로운 관점이다.

2.9 인터페이스를 짧게 유지하고 스마트(smart)를 사용하세요

인터페이스를 작게 유지하라. 그러기 위해 1,2개의 메서드에 집중하고 그러기 위해 공용적으로 사용되는 코드를 samrt 를 통해 활용해라. 이것도 꽤나 흥미로운 주제인데, interface 의 책임에 적합한가라고 하면 솔직히 잘 모르겠다... 내가 너무 정형화된 사용에 맞춰버린걸수도 있지만... 객체지향적으로 타입을 지정하는 인터페이스에 공용으로 사용될수 있는 메서드를 정해버리면 유연성 확보나 코드 수정에 있어 스마트 객체도 변경이 필요할 것이다. 그렇다면 interface 를 순수하게 쓰는 것은 아니라고 생각한다. 나는 interface 는 타입을 지정하는 용도로만 쓰고 싶다.

public interface Exchange {
    float rate(String origin, String target);

    final class Smart {
        private final Exchange origin; // 컴파일 에러 발생
        public float toUsd(String source){
            return this.origin.rate(source, "USD");
        }
    }
}
  • 추가로 예제코드 컴파일 에러 뜬다.
  • 새벽이라 머리가 안돌아간다. 근데 예제코드는 돌아가는걸로 올려주면 좋을거 같다. Yegor 씨.
profile
클린코드 지향

0개의 댓글