Test Driven Development (Ch.1 ~ 4)

Manx·2022년 11월 21일
0

TDD

목록 보기
1/4

들어가며

회사에서 TDD 스터디를 하게 되어 클린 아키텍처는 잠시 접어두기로 하였다.
TDD 스터디를 하며, 사내 코드의 테스트 코드를 짜며 테스트 코드 짜는 전반적인 느낌을 익힌 뒤
간단한 예제들을 학습하며 TDD를 배워볼 생각이다.
Code Coverage를 적용한 평가를 통해 운영 코드에 반영하기로 하였다.
Code Coverage가 무엇인지, 실제로 적용하고 테스트 코드를 조금 짜봤는데 엄청 재미있었다.
그러나, 그건 테스트 코드를 짠 것이지 테스트 주도 개발을 한 것이 아니여서 TDD를 통해 개발하면 얼마나 재밌을지 기대가 되는 하루였다. 그럼 시작


TDD의 가장 단순한 두 가지 법칙

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

TDD의 리듬

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

TDD의 목적

  • 동작하는 깔끔한 코드를 작성하는 것

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

Dollar만을 지원하던 Money객체에 다른 화폐 단위까지 적용해야 하는 경우의 예제이다.

문제 정의

$5 + 10CHF = $10 (환율이 2:1일 경우)
$5 * 2 = $10 ( 나는 이게 무슨 계산 법인지 모르겠다. 15CHF가 들어올 경우 ?)

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

할 일 :

  • amount를 private으로 만들기
  • Dollar 부작용
  • Money 반올림

테스트 코드의 초록불을 위해 일단 코딩하라

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

사소한 단계별로 문제를 밟을 수 있는 것이 능력이다.

$5 * 2 = $10 과 같이 문제를 간결하게 파악하는 능력이 가장 어렵다고 생각한다.
이러한 문제를 간결하게 정의할 수 있다면, 테스트 코드 작성에는 어려움이 없을 것이라고 생각했다.

  1. 할 일을 파악한 뒤, 테스트 코드를 먼저 작성한다.
  2. 그 후, 일단 테스트 코드가 통과하게끔 코드를 작성한다.
    -> 어떤 방식이여도 좋다. 테스트 코드가 통과만 하면 된다.

초록 막대를 본 뒤, 코드를 리팩토링 한다.
-> 이 부분에서 좋은 점을 느꼈다. 코드를 리팩토링하는 시간을 강제로 갖게 되는 부분.


Ch.2 타락한 객체

새로운 요구사항을 위해 Dollar의 인터페이스를 수정해야 하고, 그러기 위해서는 테스트도 수정해야 한다.
문제될 건 없다.
어떤 구현이 올바른가에 대한 우리 추측이 완벽하지 못한 것과 마찬가지로 올바른 인터페이스에 대한 추측 역시 절대 완벽하지 못하다.

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);
}


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

최대한 빨리 초록색을 보기 위한 전략

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

Ch.3 모두를 위한 평등

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

객체가 암시하는 것 중 하나는 모든 연산은 새 객체를 반환해야 한다는 것이다.
또다른 암시 값 객체는 equals()를 구현해야 한다는 것.

equals()와 hashcode()를 overriding해야 하는 이유

  • equals() : 기본적으로 메모리 주소가 같은지 판단하기 때문에, 동일한 값을 가지고 있어도 False를 리턴한다.
  • hashcode() : 실행 중에(Runtime) 객체의 유일한 integer값을 반환한다.
    Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환하도록 되어있다.

동일한 객체는 동일한 메모리 주소를 갖는다는 것을 의미하므로, 동일한 객체는 동일한 해시코드를 가져야 한다.
그렇기 때문에 만약 우리가 equals() 메소드를 오버라이드 한다면, hashCode() 메소드도 오버라이드 되어야 한다.


Ch.4 프라이버시

amount를 private으로 변경하는 법

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

// 1. 첫 번째 단언(assertion)을 Dollar와 Dollar를 비교하는 것으로 재작성
public void testMultiplication() {
	Dollar five = new Dollar(5);
    Dollar product = five.times(2);
    assertEquals(new Dollar(10), product.amount);
    product = five.tiems(3);
    assertEquals(new Dollar(15), product.amount);
}

// 2. product는 더이상 필요 없다.
public void testMultiplication() {
	Dollar five = new Dollar(5);
    assertEquals(new Dollar(10), five.times(2));
    assertEquals(new Dollar(15), five.times(3));
}

테스트를 고치고 나니 Dollar의 amount 인스턴스 변수를 private으로 변경할 수 있었다.

처음에 10을 왜 굳이 new Dollar(10)으로 고치나 했는데, amount가 깔끔하게 제거되어 놀랐다!


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

profile
백엔드 개발자

0개의 댓글