만쥬의 개발일기
article thumbnail
Published 2023. 8. 3. 13:03
[TDD] - 1장 ~ 3장 📖BOOK/📙TDD

들어가는 장

권장하는 TDD 훈련 방법

  • 간단하고 쉬운 문제들을 TDD로 시도한다. 가능하면 전에 접하고, 프로그래밍해본 문제가 좋다.
  • 초록 막대 주기(이전 초록 막대와 다음 초록 막대 사이의 간격)는 가능하면 짧도록 한다.
  • 초록 막대 주기의 최대 시간을 정하고, 이를 초과하면 이전 초록 막대 상태로 돌리고 새로 시작한다.
  • '진짜로 만들기 전까지만 가짜로 구현하기' 를 적극적으로 사용하려고 노력한다.
  • 같은 문제를 여러번풀어본다.
  • 초기에는 리팩토링 툴을 사용하지 않는 것이 좋다.

개발을 할 때 하항상 다음 두가지 법칙을 따른다.

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

뱅크 샐러드 개발자님과의 만남

최근 실제로 사내 대부분의 IOS 사업부에서 사용할 스위프트 자동화 테스트 툴을 만들어 배포하고, TDD를

적극 선호하는 개발자님과 만나 사견을 들을 기회가 있었다. 그 분이 강조하신 사항은 다음과 같다.

TDD로 개발했을 때 좋은 점은?

버그를 잡기 위해서 ❌

문서 작성에 매우 도움이 된다

TDD로 개발을 할 때 버그를 잡고자 하는 기대는 하지 말자. 시간에 비해 효과 X

TDD는 나의 코드의 엄밀성을 증명하는 문서를 작성할 때 큰 도움이 된다.

EXTREME PROGRAMMING

결국은 TDD도 CI/CD 등과 같은 더 잦은 양질의 피드백을 사람이 아닌 머신으로부터 얻기 위한

익스트림 프로그래밍의 한 방법론이다. TDD에 너무 목매지 말 것.

애자일에서의 단위 테스트는 유저의 스토리를 기반으로 해야 한다.

예를 들어 버튼을 눌렀을 때 작동해야 하는 일이라던가 , 스와이프를 했을 때의 반응 등등.

이는 관점에 따라 (변수 , 클래스 혹은 프로그램 까지) 통합 테스트로 보일 수도, 단위 테스트로 보일 수도 있다.

TDD를 실제로 경험해보는 법

9월에 인프런에 강좌를 낼 예정이다. (홍보인가?)

신입 개발자를 뽑을 때

내 코드의 무결성을 테스트 코드로 증명할 수 있다면 BEST이다.

QA 자격증인 ISTQB 또한 큰 도움이 된다.

1부 화폐 예제

TDD의 리듬

  1. 재빨리 테스트를 하나 추가 (가짜로 구현)
  2. 모든 테스트를 실행하고, 새로 추가한 것의 실패 여부 확인
  3. 코드 수정
  4. 모든 테스트를 실행하고, 전부 성공하는지 확인
  5. 리팩토링을 통한 중복 제거

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

다음과 같은 MONEY 객체가 있다고 가정해보자.

이를 다중 통화를 지원하는 보고서를 만들기 위해선 통화 단위를 추가하는 등 수정이 불가피하다.

객체를 만들면서 시작하는 것이 아닌 테스트를 먼저 만들어야 한다.

가장 빨리 간단한 테스트를 위해, 주식 개수만큼 값을 곱해 금액을 구하는 함수를 만들어보자 .

 public void testMultiplication() {
     Dollar five = new Dollar(5);
     five.times(2);
     assertEquals(10, five.amount);
 }

위 코드를 작성할 때 다음과 같은 부분들을 생각해야 한다.

  • $5 + 10CHF = $10 (환율이 2:1일 경우)
  • $5 X 2 = $ 10 (가지고 있는 주식 수 만큼 가격 계산)
  • amount를 private으로 만들기
  • Dollar 부작용?
  • money 반올림?

그리고 위 코드를 실행하면 다음 컴파일 에러들이 발생한다.

  • Dollar 클래스가 없음
  • 생성자가 없음
  • times(int) 메소드가 없음
  • amount 필드가 없음

우선 빨간 막대를 빠르게 보기 위해, 컴파일 에러부터 해결한다.

 public class Dollar {
     int amount;

     Dollar(int amount) {
     }

     void times(int multiplier) {
     }
 }

테스트를 실행하면, 결과로 10이 나와야 하지만 0이 나오기 때문에, 빨간 막대를 보게 된다.

최소한의 작업으로 초록 막대를 보려면 다음과 같이 수정한다.

 int amount = 10;
  1. 재빨리 테스트를 하나 추가 (가짜로 구현)
  2. 모든 테스트를 실행하고, 새로 추가한 것의 실패 여부 확인
  3. 코드 수정
  4. 모든 테스트를 실행하고, 전부 성공하는지 확인
  5. 리팩토링을 통한 중복 제거

이제 위 과정중 4번까지 성공한다.

그렇다면 리팩토링을 통해 중복을 제거해보자.

중복은 도대체 어디서 발생한 것일까?

 int amount = 5 * 2

10을 5 * 2로 바꾸면 중복이 보인다. 5와 2라는 데이터가 테스트 코드와 클래스에 중복이 되는 것이다.

이를 제거하기 위해 생성자와 times를 새롭게 정의하자.

 public class Dollar {
     int amount;

     Dollar(int amount) {
         this.amount = amount;
     }

     void times(int multiplier) {
         amount *= multiplier;
     }
 }

이제 중복은 제거되고, 첫번째 테스트는 완벽히 통과되었다.

  • $5 + 10CHF = $10 (환율이 2:1일 경우)
  • $5 X 2 = $ 10 (가지고 있는 주식 수 만큼 가격 계산)
  • amount를 private으로 만들기
  • Dollar 부작용?
  • money 반올림?

지금까지 한 작업은 다음과 같다.

  • 알고 있는, 작업해야 할 테스트 목록 만들기
  • 오퍼레이션이 외부에서 어떻게 보이길 원하는지 코드로표현
  • junit 상세 사항무시
  • 스텁 구현(구현되지 않은 함수 임의 생성)을 통해 테스트 컴파일
  • 끔찍한 구현(하드 코딩) 을 통해 테스트 통과
  • 상수를 변수로 변경하며 점진적 일반화
  • 새로운 할일들을 처리하는 대신 할 일 목록에 추가하고 넘어가기

2장에서는 Dollar 부작용에 관한 작업을 표현해보자.

2장 타락한 객체

현재까지 짠 코드에서는 Dollar에 대한 연산을 수행하고 나면 Dollar의 값이 바뀌고 만다.

다음 테스트를 살펴보자.

 public void testMultiplication() {
     Dollar five = new Dollar(5);
     five.times(2);
     assertEquals(10, five.amount);
     five.times(3);
     assertEquals(15, five.amount);
 }

한번 times를 실행하고 나면, 객체는 값이 바뀌고 오염되므로, 새로운 객체를 반환하기를 원한다.

 public void testMultiplication() {
     Dollar five = new Dollar(5);
     Dollar product = five.times(2);
     assertEquals(10, product.amount);
     product = five.times(3);
     assertEquals(15, product.amount);
 }

이렇게 수정할 경우, 수량에 따른 개수를 계산은 product 객체에 위임하게 되므로, five 객체는 오염되지 않을 수 있다.

하지만 기존 코드로는 컴파일이 안되므로 , times를 수정해주자.

     Dollar times(int multiplier) {
         amount *= multiplier;
         return null;
     }

이제 컴파일은 성공하지만, 실행은 되지 않는다.

올바른 값을 반환해 테스트를 통과하게 해주자.

     Dollar times(int multiplier) {
         return new Dollar(amount*multiplier);
     }

이제 테스트는 통과했고, 테스트 두개를 무사히 해결했다.

  • $5 + 10CHF = $10 (환율이 2:1일 경우)
  • $5 X 2 = $ 10 (가지고 있는 주식 수 만큼 가격 계산)
  • amount를 private으로 만들기
  • Dollar 부작용?
  • money 반올림?

지금까지 해온 TDD의 흐름은 다음과 같다.

  • 설계상의 결함을 그 결함으로 인해 실패하는 테스트로 변환했다.
  • 스텁 구현으로 빠르게 컴파일을 통과하도록 했다.
  • 올바르다고 생각하는 코드를 입력해 테스트를 통과한다.

3장 모두를 위한 평등

객체의 값(정확히는 amount) 를 비교해야 하기 때문에 equals 구현
모든 인스턴스 변수들이 그렇듯이 amount를 private로 만들기 위해 필요.

Dollar를 해시 테이블의 키로 쓸 경우엔 , hashCode()도 구현 필요.
(다만, 바로 작업하지 않고 할일 목록에 적어둔다.)

테스트 코드 작성

public void testEquality() {
    assertTrue(new Dollar(5).equals(new Dollar(5)));
}

빨간 막대 제거를 위한 가짜 구현

public boolean equals(Object object) {
    return true;
}

삼각 측량이란 ?

쉽게 말해 예제가 두개 이상 있어야지만 코드를 일반화 할 수 있다는 것.
$5 == $5 로 true 만 검사하는 것이 아니라
$5 != $6와 같이 false 도 검사할 필요가 있다.

public void testEquality() {
    assertTrue(new Dollar(5).equals(new Dollar(5)));
    assertFalse(new Dollar(5).equals(new Dollar(6)));
}

equality 일반화

public boolean equals(Object object) {
    Dollar dollar = (Dollar) object;
    return amount == dollar.amount;
}

동일성 문제는 일시적으로 해결했으나,
널 값이나 다른 객체와 비교할 때는 해결 x
따라서 할일 목록에 다음 추가

  • Equal null
  • Equal objcet

이 장에서 한 작업들 검토

  • 오퍼레이션을 테스트로 작성했다.
  • 오퍼레이션을 간단히 구현했다. (항상 true를 반환하는 스텁)
  • 곧장 리팩토링 하는 대신 테스트를 좀 더 보완했다.
  • 두 경우를 모두 수용할 수 있도록 리팩토링했다.

'📖BOOK > 📙TDD' 카테고리의 다른 글

[TDD] 18장 ~ 21장  (0) 2023.09.04
profile

만쥬의 개발일기

@KangManJoo

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!