박종훈 기술블로그

테스트 주도 개발 (TDD) 스터디 1회차 (1-8장)

테스트 주도 개발 - 켄트 벡

tdd-korean-book-cover


품질에 대한 챔임을 그 누구보다도 작업자에게 맞겨야 한다. 작업자가 정보를 제일 많이 알고 있고, 또 최종 결과에 직접적 영향을 가장 많이 끼칠 수 있기 때문이다.

당신이 천재라면 당신에게 이런 법칙은 필요 없다. 또 당신이 멍청이라면 역시 이런 법칙이 도움이 되지 않을 것이다. 그 사이에 존재하는 대다수의 사람들은 다음 두 가지 단순한 법칙을 따름으로써 잠재력을 한껏 발휘할 수 있다.

  • 어떤 코드건 작성하기 전에 실패하는 자동화된 테스트를 작성하라.
  • 중복을 제거하라.

이야기 해볼 거리

  • TDD 수율
  • 노트 작성?
  • 작은 증가분 : increments (변경사항 정도로 이해하면 되려나)

1부 : 화폐 예제

1부에서는 완전히 테스트에 의해 주도되는 전형적 모델 코드를 개발할 것이다.

목표는 테스트 주도 개발(TDD)의 리듬을 보도록 하는 것이다. 그 리듬은 다음과 같이 요약할 수 있다.

  1. 재빨리 테스트를 하나 추가한다.
  2. 모든 테스트를 실행하고 새로 추가한 것이 실패하는지 확인한다.
  3. 코드를 조금 바꾼다.
  4. 모든 테스트를 실행하고 전부 성공하는지 확인한다.
  5. 리팩토링을 통해 중복을 제거한다.

1장. 다중 통화를 지원하는 Money 객체

TDD의 핵심은 작은 단계를 밟아 나가는 것이다. 작은 단계로 작업하는 방법을 배우면, 저절로 적절한 크기의 단계로 작업할 수 있을 것이다.

  • 우리가 알고 있는 작업해야 할 테스트 목록을 만든다.
  • 오퍼레이션이 외부에서 어떻게 보이길 원하는지 말해주는 이야기를 코드로 표현했다.
  • JUnit에 대한 상세한 사항들은 잠시 무시한다.
  • 스텁 구현을 통해 테스트를 컴파일한다.
  • 끔찍한 죄악을 범하여 테스트를 통과시킨다.
  • 돌아가는 코드에서 상수를 변수로 변경하여 점진적으로 일반화했다.
  • 새로운 할일들은 한번에 처리하는 대신 할일 목록에 추가하고 넘어갔다.

이야기 해볼 거리

  • 최대한 빠르게 성공하게 하기

    • 비록 하드코딩이라 할지라도. : 이 부분에서 개발자들이 어색함을 느낄 것 같음.
    • 이 부분은 다음장에서도 언급됨
  • 의존성

    • 깨지기 쉬운 테스트 : 한쪽을 수정하면 다른 한쪽도 수정해야함.
    • 테스트에 로직을 담지 않기.

2장. 타락한 객체

일반적인 TDD 주기는 다음과 같다.

  1. 테스트를 작성한다. 마음속에 있는 오퍼레이션이 코드에 어떤 식으로 나타나길 원하는지 생각해보라. 이야기를 써내려가는 것이다. 원하는 인터페이스를 개발하고 올바른 답을 얻기 위해 필요한 이야기의 모든 요소를 포함시켜라.
  2. 실행 가능하게 만든다. 무엇보다 중요한 것은 빨리 초록 막대를 보는 것이다. 깔끔하고 단순한 해법이 명백히 보인다면 그것을 입력해라. 만약 깔끔하고 단순한 해법이 있지만 구현하는 데 몇 분 정도 걸릴 것 같으면 일단 적어 놓은 뒤에 원래 문제(초록 막대를 보는 것)로 돌아오자. 미적인 문제에 대한 이러한 전환은 몇몇 숙련된 소프트웨어 공학자들에게는 어려운 일이다. 그들은 오로지 좋은 공학적 규칙들을 따르는 방법만 알 뿐이다. 빨리 초록 막대를 보는 것은 모든 죄를 사해준다. 하지만 아주 잠시 동안만이다.
  3. 올바르게 만든다. 이제 시스템이 작동하므로 직전에 저질렀던 죄악을 수습하자. 좁고 올곧은 소프트웨어 정의(software righteousness)의 길로 되돌아와서 중복을 제거하고 초록 막대로 되돌리자.

우리의 목표는 작동하는 깔끔한 코드를 얻는 것이다. 이 목표는 최고의 프로그래머들조차 도달하기 힘든 목표이다. 그렇다면 나누어 정복하자(divide and conquer). 일단 ‘작동하는 깔끔한 코드’를 언어야 한다는 전체 문서 중에서 ‘작동하는’에 해당하는 부분을 먼저 해결하라. 그리고 나서 ‘깔끔한 코드’ 부분을 해결하는 것이다.

최대한 빨리 초록색을 보기 위해 취할 수 있는 전략

  • 가짜로 구현하기: 상수를 반환하게 만들고 진짜 코드를 얻을 때까지 단계적으로 상수를 변수로 바꾸어 간다.
  • 명백한 구현 사용하기: 실제 구현을 입력한다.
  • 삼각측량: 3장에서 알아본다.

3장. 모두를 위한 평등

값 객체 패턴 (value object pattern) : 객체를 값처럼 쓸 수 있도록 하는 것
값 객체에 대한 제약사항 중 하나는 객체의 인스턴스 변수가 생성자를 통해서 일단 설정된 후에는 결코 변하지 않는다는 것이다.

vo pattern을 사용하면 별칭 문제 - AliasingBug를 방지할 수 있다.

삼각측량

예제가 2개 이상 있을 때 코드를 일반화 할 수 있다. 이를 위해 테스트 케이스를 하나 더 추가한다.
어떻게 구현/리팩토링해야하는지 전혀 감이 오지 않을때 사용하면 좋음. 곧장 리팩토링 하는 대신 테스트를 조금 더 추가하고 다른 테스트까지 수용할 수 있도록 리팩토링 함.

이야기 해볼 거리

  • 불변객체
    • java date
      • Calendar : 불변객체 아님
      • LocalDate, LocalTime : 불변객체 java8

4장 프라이버시

TDD는 완벽함을 위해 노력하지는 않는다. 모든 것을 두 번 말함으로써(코드와 테스트로 한 번씩) 자신감을 가지고 전진할 수 있을 만큼만 결함의 정도를 낮추기를 희망할 뿐이다.

때때로 우리의 추론이 맞지 않아서 결함이 손가락 사이로 빠져나가는 수가 있다. 그럴 때면 테스트를 어떻게 작성해야 했는지에 대한 교훈을 얻고 다시 앞으로 나가가자.

5장. 솔직히 말하자면

  • 큰 테스트를 한번에 공략할 수는 없다. 그래서 진전을 나타낼 수 있는 자그마한 테스트를 만들었다.
  • (누군가가 보기에는) 뻔뻔스럽게도 중복을 만들고 조금 고쳐서 테스트를 작성했다.
  • 중복이 사라지기 전에는 집에 가지 않을 것이다.

6장. 돌아온 ‘모두를 위한 평등’

충분한 테스트가 없다면 테스트가 갖춰지지 않은 리팩토링을 할 수 밖에 없다. 그렇게 되면 리팩토링하면서 실수했는데도 불구하고 테스트가 여전히 통과할 수도 있다.

있어야 할 것 같은 테스트를 작성하라. 그렇게 하지 않으면 결국에는 리팩토링하다가 뭔가 깨트릴 것이다. 그러면 여러분은 리팩토링에 대해 안 좋은 느낌을 갖게 되고, 리팩토링을 덜 하게 된다. 리팩토링을 더 적게 하면 설꼐의 질이 저하되고, 결국 여러분은 해고될 것이다. 리팩토링 전에 테스트를 하자.

7장. 사과와 오렌지

you can’t compare apples and oranges : 서로 다른 것을 비교할 수 없다.

다른 하위 클래스간의 equals에서 true가 발생하는 것을 막기 위해 getClass()를 통한 비교 추가

8장. 객체 만들기

팩토리 메서드를 도입하여 테스트 코드에서 콘크리트 하위 클래스의 존재 사실을 분리

이야기 해볼 거리

테스트를 통해 코드 설계를 개선해나감.

Well Grounded Java Developer
테스트 주도 개발의 기본 전제는 테스트를 구현하기 전에 작성하자는 것입니다. 이를 통해 테스트가 코드 설계에 영향을 미칩니다.
테스트가 설계와 구현을 주도할 것입니다.

구글 엔지니어는 이렇게 일한다
테스트를 작성하는 행위는 시스템의 설계도 개선해줍니다. 테스트는 시스템 코드의 첫번째 고객이라는 자격으로 여러분이 설계한 설계에 관해 많은 이야기를 해줍니다. 데이터베이스에 너무 강하게 묶여 있는지. 이 API가 필수 유스케이스를 지원하는지, 시스템이 극단적인 상황들에 잘 대처하는지 등을 확인할 수 있습니다.