Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- mp4fpsmod
- taint
- kotlin
- PersistenceContext
- JanusWebRTCGateway
- python
- Spring Batch
- vfr video
- 코루틴 빌더
- 달인막창
- JanusGateway
- 자원부족
- PytestPluginManager
- table not found
- 티스토리챌린지
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- preemption #
- JanusWebRTCServer
- Value too long for column
- tolerated
- k8s #kubernetes #쿠버네티스
- 깡돼후
- 코루틴 컨텍스트
- pytest
- 오블완
- terminal
- 겨울 부산
- JanusWebRTC
- 개성국밥
- VARCHAR (1)
Archives
너와 나의 스토리
TDD(Test-Driven Development) 연습해보기 - 예제 1: Money (1) 1~8장 본문
개발/TDD(Test-Driven Development)
TDD(Test-Driven Development) 연습해보기 - 예제 1: Money (1) 1~8장
노는게제일좋아! 2021. 2. 25. 10:44반응형
"테스트 주도 개발 Test-Driven Development: By Example" 책에 나오는 예제를 실제로 구현해보자.
프로그래밍 순서
- 빨강 - 실패하는 작은 테스트를 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
- 초록 - 빨리 테스트가 통과하게끔 만든다. 이를 위해 어떤 죄악을 저질러도 좋다.
- 죄악: 테스트 통과만을 위해 복붙 하거나 함수에서 임의의 값을 무조건 리턴하게 구현하는 것.
- 리팩토링 - 2단계에서 생겨난 모든 중복을 제거한다.
예제 1: Money
- 다중 통화를 지원하는 보고서를 만들어보자.
종목 | 주 | 가격 | 합계 |
IBM | 1000 | 25USD | 25000USD |
Novartis | 400 | 150CHF | 60000CHF |
합계 | 65000USD |
기준 | 변환 | 환율 |
CHF | USD | 1.5 |
- 이러한 보고서를 생성하려면 어떤 기능들이 필요할까?
- 통화가 다른 두 금액을 더해서 주어진 환율에 맞게 변한 금액을 결과로 얻을 수 있어야 한다.
- 어떤 금액(주가)을 어떤 수(주식의 수)에 곱한 금액을 결과로 얻을 수 있어야 한다.
1. $5 + 10CHF = $10 (환율이 2:1일 경우)
2. $5 x 2 = $10
- 위 요구사항을 구현하기 앞서 먼저 테스트를 만들자.
- 1번보다 2번 기능이 간단해 보이므로 2번부터 테스트 코드를 작성해보자.
Test 1: 어떤 금액(주가)을 어떤 수(주식의 수)에 곱한 금액을 결과로 얻을 수 있어야 한다.
- 간단한 곱셈을 수행하는 테스트 코드를 작성하자
- 위 코드에서는 4개의 컴파일 에러가 발생한다.
- Dollar 클래스 없음
- 생성자 없음
- times(int) 메서드가 없음
- amount 필드가 없음
1. Dollar 클래스 정의
2. 생성자 생성
3. times 메서드 생성
4. amount 필드 추가
- 이제 컴파일이 가능하다. 그리고 다음과 같이 테스트가 실패하는 모습을 볼 수 있다.
- 테스트가 성공하도록 수정해보자.
- 원하는 기능을 하도록 수정해보자.
- 하지만 아직 Dollar에 대한 문제점이 있다.
- times 메서드를 수행하면 amount 값이 변경되는 것이다.
- 올바른 금액을 갖는 새로운 Dollar를 반환하도록 수정하자.
- 위 테스트 코드를 다음과 같이 수정할 수 있다.
- 위 테스트 코드를 돌리면 객체 자체는 다르기 때문에 다음과 같이 에러가 발생하게 된다.
- 이를 위해, 동치성을 일반화해줄 equals 메서드를 작성해주면, 테스트가 성공하게 된다.
- 수정된 테스트 코드에서는 더 이상 amount 변수를 직접 사용하는 일이 없어졌다.
- 즉, 변수를 private으로 변경할 수 있다.
Test 2: 통화가 다른 두 금액을 더해서 주어진 환율에 맞게 변한 금액을 결과로 얻을 수 있어야 한다.
- 예: $5 + 10CHF = $10 (환율이 2:1일 경우)
- Dollar 객체와 비슷하지만 달러 대신 Franc을 표현할 수 있는 객체가 만들어 단위가 섞인 덧셈 테스트를 작성하고 돌려보자.
리팩토링
<프로그래밍 순서>
1. 빨강 - 실패하는 작은 테스트를 작성한다. 처음에는 컴파일조차 되지 않을 수 있다.
2. 초록 - 빨리 테스트가 통과하게끔 만든다. 이를 위해 어떤 죄악을 저질러도 좋다.
3. 리팩토링 - 2단계에서 생겨난 모든 중복을 제거한다.
- 이제 테스트는 통과하니 리팩토링을 해보자.
- Dallar와 Franc 클래스의 공통 상위 클래스를 생성하자. -> Money 클래스
- 그리고 Money 클래스가 공통의 equals 코드를 갖게 하자.
- Money 클래스를 생성해주고, 하위 클래스에서 변수를 볼 수 있도록 amount 변수를 protected로 변경하자.
- 이제 equals() 코드를 Money로 올려보자 -> 상위 클래스에서만 해당 메서드를 선언함으로써 중복을 줄이자.
- Franc/Dollar.equals()와 Money.equals()는 거의 비슷해 보인다. 이 두 부분을 완전히 똑같이 만들 수 있다면 프로그램의 의미를 변화시키지 않고도 Franc/Dolloar의 equals()를 지워버릴 수 있다.
- 이 경우에도 동치성 판단을 제대로 하는지 확인하기 위해 테스트 코드를 작성해보자.
- 두 클래스의 equals() 지우고, 위 테스트 돌리면 성공한다. 하지만 적절한 테스트가 아니다. 왜냐하면, 서로 다른 통화를 비교하는 테스트가 존재하지 않기 때문이다.
- 두 클래스에서 amount 변수도 지워주자.
- 즉, assertFalse(new Franc(5).equals(new Dollar(5))); 테스트 코드를 추가하면 테스트는 실패한다.
- 오직 금액과 클래스가 서로 동일할 때만 두 Money가 같다고 판단해야 한다.
- 이처럼, 적절하지 못한 테스트 코드를 작성하면 리팩토링 과정에서 실수했는데도 불구하고 테스트가 통과해 오류를 탐지하지 못할 수도 있다.
- equals() 메서드를 다음과 같이 수정하면 아래 테스트가 성공하는 것을 확인할 수 있다.
이번에는 공통 코드 부분인 times()를 해결할 차례이다.
- 양쪽 모두 Money를 반환하게 만들면, 더 비슷하게 만들 수 있다.
객체 만들기
Dollar, Franc 두 클래스의 times() 구현 코드가 거의 똑같다. Money의 두 하위 클래스는 그다지 많은 일을 하는 것 같지 않으므로 아예 제거해버리고 싶다.
- 하위 클래스에 대한 직접적인 참조를 줄여보자 -> 하위 클래스를 제거하기 위해 한 발짝 다가서자.
- 먼저, Money에 Dollar를 반환하는 팩토리 메서드(factory method)를 도입할 수 있다.
- factory method: 부모 클래스에 알려지지 않은 구체 클래스를 생성하는 패턴이며, 자식 클래스가 어떤 객체를 생성할지를 결정하도록 하는 패턴이다.
- Dollar에 대한 참조가 사라지게 하기 위해 테스트의 선언부를 다음과 같이 바꿔보자.
- 이제(와서?) Money를 추상 클래스로 변경한 후 times() 메서드를 선언하자.
- 그럼, 위의 테스트를 돌리면 성공할 것이다.
- 이제 팩토리 메서드를 테스트 코드의 나머지 모든 곳에서 사용할 수 있다.
- 이제 클라이언트 코드에서 Dollar 클래스 존재를 알지 못한다. 하위 클래스의 존재를 테스트에서 분리함으로써 어떤 모델 코드에도 영향을 주지 않고 상속 구조를 마음대로 변경할 수 있게 됐다.
- Franc도 Dollar처럼 수정해주자.
- 정리:
- 동일한 메서드 times()의 두 변이형 메서드 서명부를 통일시킴으로써 중복 제거를 향해 한 단계 더 전진했다.
- 최소한 메서드 선언부만이라도 공통 상위 클래스로 옮겼다.
- 팩토리 메서드를 도입하여 테스트 코드에서 콘크리트 하위 클래스의 존재 사실을 숨길 수 있었다.
- 콘크리트(Concrete) 클래스: new 키워드를 사용하여 인스턴스를 만드는 클래스
출처:
- [테스트 주도 개발 Test-Driven Development: By Example"]
반응형
'개발 > TDD(Test-Driven Development)' 카테고리의 다른 글
TDD(Test-Driven Development) 테스트 주도 개발 패턴 정리 (2) | 2021.03.31 |
---|---|
TDD(Test-Driven Development) 연습해보기 - 예제 1: Money (3) 12~17장 (0) | 2021.03.11 |
TDD(Test-Driven Development) 연습해보기 - 예제 1: Money (2) 9~11장 하위 클래스 제거하기 (0) | 2021.02.25 |
Comments