SlideShare une entreprise Scribd logo
1  sur  35
This report is written by Sunny Kwak. (sunnykwak@hanmail.net, sunnykwak.tistory.com)
2016.03.07
Test Driven Development
(mini-lesson)
Version 0.2
1
Test Driven Development – Video Shop
Test Driven Development 접근하기
Test Driven Development
(살살) 접근하기
2
Test Driven Development – Video Shop
Test Driven Development 접근하기
 프로그래밍은 주어진 과제를 해결하는 것이다.
 최초 과제 혹은 요구사항은 대체로 단순하다.
 하지만, 점점 요구가 복잡해진다.
(간혹 처음부터 조건이 까다로운 경우도…)
 일단 쏴서 맞추어 봐라. 그래서 시키는 대로 했다.
 점점 앉아쏴, 서서 쏴, 업드려 쏴… 설마 덤블링 하면서 쏘라고 하나?
목표를 쏴라~
3
Test Driven Development – Video Shop
Test Driven Development 접근하기
Non TDD
문제를 더 작은 문제로 쪼갠다.
각각의 작은 문제를 해결하기 위한 모듈(객체)들과 각 모듈간의 관계(상속, 합성)를 정의한다.
그리고, 문제를 해결하기 위한 코드를 작성한다. (원칙은 그렇다고 한다.)
문제를 더 작은 문제로 쪼갠다.
하나의 컴포넌트를 선언하고, 작은 문제 하나를 푼다.
그리고, 또 다른 문제를 해결하기 위해 기존 컴포넌트에 코드를 계속 추가한다.
필요한 기능을 추가할 수록 점점 스파게티 코드가 되어간다.
리팩토링을 해서 코드를 개선하기도 하지만…
테스트가 점점 어려워진다. 하나의 컴포넌트가 여러가지 이상의 기능을 수행하면서
최초에 테스트 했던 기능이 동작하지 않는다.
그리고, 점점 억울해진다. “아니, 이거 원래 잘 동작하던 건데요? 미치겠네..”
선배도 답답하고, 고객은 열받고, 개발자는 집에 가고 싶을 뿐이고~
사실은?
4
Test Driven Development – Video Shop
Test Driven Development 접근하기
Test Driven Development!
작은 목표 혹은 문제를 선정한다.
문제에 대한 답을 예상한다. 그리고, 예상 결과를 검사하는 코드를 먼저 작성한다.
결과를 반환하는 컴포넌트를 선언하고, 간단히 결과를 생성하는 실행 코드를 작성한다.
위 과정을 반복하면서 필요하다면, 리팩토링을 하며 컴포넌트를 추가하거나 구조를 변경한다.
그래서?
요구사항 혹은 조건이 추가되거나, 기능을 추가했을 때 이전에 동작하던 기능이
잘 동작하는지 테스트 케이스가 자동으로 확인해 준다.
대체로 잘 동작하는 상태로 업무가 진행된다. 자동화된 테스트를 통해 문제
여부를 빠르게 확인할 수 있어서 테스트 시간과 심리적 부담이 감소한다.
늘 동작하는 코드를 보면서 작업하게 되니. 한참 만들어 놓고, 동작 안하는
코드를 보고 절망할 일이 없다. 코드 분량이 늘어나도 적극적으로 코드를 수정할
수 있게 된다.
정시 퇴근이 (어쩌면) 가능할지도 모른다.
문제의 범위를 크게 잡지 말고, 조금씩 기능을 개선하며 리듬을 타야 한다.
5
Test Driven Development – Video Shop
퀴즈 - VideoShop
Test Driven Development
퀴즈 풀기
6
Test Driven Development – Video Shop
퀴즈 - VideoShop
고객
비디오테이프
대여 서비스
Video Shop
 고객은 이름을 가지고 있다.
 고객은 한번에 여러 개의 비디오를 대여할 수 있고, 각각의 대여 기간은 일정하지 않다.
 비디오는 3가지 유형이 있다. (영화, 스포츠, 다큐멘터리)
 각각의 비디오는 독립적인 일일 대여요금이 부여되며, 유형에 따라 할인율이 틀리다.
 비디오 대여 시 유형에 따라 다른 포인트를 제공한다.
 비디오 대여가 발생할 때, 포인트를 적립하고 누적 포인트를 조회할 수 있어야 한다.
 상세 대여 내역 (종류, 제목, 가격 등)을 조회할 수 있어야 한다.
 대여 기간에 따라 요금을 계산할 수 있어야 한다.
기타 조건
 TDD의 순서를 따라서 개발할 것.
 테스트할 목록을 작성하라.
7
Test Driven Development – Video Shop
퀴즈 - VideoShop
문제가 너무 커 보이는데요?
어디서 부터 접근해야 할까요?
문제를 먼저 쪼개는 작업이 필요하지 않나요?
자칫 엉뚱한 곳부터 풀다가 헤매는 건 아닐까요?
저는 TDD를 처음 경험해 보는데 조언 해주실 건 없나요?
어디서 시작하나
상관 없는 게 TDD 입니다.
두려워하지 마시고 일단
시작해 보세요!
Just Do IT!
8
Test Driven Development – Video Shop
퀴즈 - VideoShop
그래서, 그냥 순서대로 풀었습니다!
- 첫번째 문제 혹은 태스크 ‘고객은 이름을 가지고 있다.’
- 이게 전부는 아니죠… 새로운 고객을 등록하고, 이름으로 검색할 수 있어야 합니다.
- 그래서, 아래와 같이 작업했습니다.
 개발 환경
- 이클립스를 실행한 후 워크스페이스를 만들고, VideoShop 프로젝트를 생성했습니다
 패키지 선언
- 테스트 코드와 실행 코드는 다른 패키지에 위치 시켜야 한답니다.
- 그래서, com.acme.videoshop 패키지와 test.acme.video.shop 으로 분리했습니다.
 테스트 프레임워크 설정
- 다양한 도구가 있지만, 이클립스에 기본으로 포함되어 있는 Junit을 선택했습니다.
 테스트 클래스 선언
- test.acme.videoshop. CustomerTest 클래스를 작성합니다.
9
Test Driven Development – Video Shop
퀴즈 - VideoShop
고객 관리를 테스트 하기 위한 CustomerTest 클래스 작성.
package test.acme.videoshop;
import com.acme.videoshop.customer.Customer;
import com.acme.videoshop.customer.CustomerManager;
import junit.framework.TestCase;
public class CustomerTest extends TestCase {
private static final String CUSTOMER_NAME = "Sunny Kwak";
public void testCustomer() {
Customer customer = CustomerManager.register( CUSTOMER_NAME );
assertNotNull(customer);
customer = CustomerManager.lookup(CUSTOMER_NAME);
assertNotNull(customer);
customer = CustomerManager.lookup(CUSTOMER_NAME + ".");
assertNull(customer);
}
}
10
Test Driven Development – Video Shop
퀴즈 - VideoShop
Customer & CustomerManager
Customer
+name
+Customer(String name)
+getName()
CustomerManager
+customerMap
+register(String customerName)
+lookup(String customerName)
고객 객체를 직접 생성하지 않고, CustomerManager 클래스의 register() 메소드를 통해 간접 생성.
고객의 이름 검색 또한 lookup() 메소드를 통해 수행.
- 고객 관리에 대한 모든 책임을 CustomerManager에 위임.
- 고객 정보를 저장소 등으로 보내거나, 읽어오게끔 변경하더라도 CustomerManager 외부에서는 변경 사항을
알아차리지 못하게 됨.
- 이를 통해 정보 은폐(information hiding)을 시도함.
11
Test Driven Development – Video Shop
퀴즈 - VideoShop
Customer & CustomerManager
Customer
+name
+Customer(String name)
+getName()
CustomerManager
+customerMap
+register(String customerName)
+lookup(String customerName)
- Customer 객체를 CustomerManager의 register() 메소드 내부에서 생성하고 있습니다.
- 고객에 대한 정보(항목)가 늘어날 수도 있으니 Customer 객체를 생성한 후에 register() 메소드에 Customer
객체를 파라미터로 전달하는 건 어떨까요?
- 만일 구조를 바꾸게 된다면, CustomerManager가 Customer 객체를 생성하는 경우와 그렇지 않을 경우 장단
점은 무엇일까요?
확실한 답을 얻기 위해 질문하는 것이 아닙니다. 어차피 정답이 없을 수도 없지만,
생각하는 습관을 들이고, 남의 의견을 경청하기 위한 훈련입니다.
멍청한 질문이 되지 않을까 두려워 하면 아무 것도 배우지 못합니다!
12
Test Driven Development – Video Shop
퀴즈 - VideoShop
그리고, 두번째 태스크를 선택 했습니다.
- 내용은 거의 유사합니다. ‘비디오 상품 관리’
- 비디오 유형에 따라 분리해 등록하고, 제목으로 검색할 수 있어야 합니다.
- 비디오 테이프마다 대여료가 다르다고 하는군요.
 테스트 케이스 클래스 생성
- VideoTapeTest 클래스를 작성합니다.
 테스트 메소드 생성
- testVideo() 메소드를 작성했습니다.
- 비디오 테이프를 등록하고, 정상적으로 등록되었는지 등록된 내용을 검사합니다.
- 비디오 테이프를 등록 한후 타이틀로 검색하고, 존재하는지 검사합니다.
 실행 코드 작성
- VideoCatalog, Video 클래스를 생성합니다.
13
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 목록 관리를 테스트 하기 위한 VideoTest 클래스.
package test.acme.videoshop;
import com.acme.videoshop.video.Video;
import com.acme.videoshop.video.VideoCatalog;
import junit.framework.TestCase;
public class VideoTapeTest extends TestCase {
public static String STAR_WARS = "StarWars";
public void testVideo() {
Video item = VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100);
assertNotNull(item);
assertEquals(item.getTitle(), STAR_WARS);
assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE);
assertEquals(item.getRent(), 100);
item = VideoCatalog.lookup(STAR_WARS);
assertNotNull(item);
assertEquals(item.getTitle(), STAR_WARS);
assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE);
assertEquals(item.getRent(), 100);
item = VideoCatalog.lookup(STAR_WARS + ".");
assertNull(item);
}
}
14
Test Driven Development – Video Shop
퀴즈 - VideoShop
Video & VideoCatalog
비디오 객체를 직접 생성하지 않고, VideoCatalog 클래스의 register() 메소드를 통해 간접 생성.
비디오 검색 또한 lookup() 메소드를 통해 수행.
-비디오 유형을 표현하기 위해서 상속하는 방법이 있지만, 불필요한 클래스 추가라고 판단되어 enum 선언
public enum VIDEO_TYPE {
MOVIE, SPORTS, DOCUMENTARY
};
Video
+title
+type
+rent
+Video(title, type, rent)
+getTitle()
+getType()
+getRent()
VideoCatalog
+catalog
+register(title, type, rent)
+lookup()
15
Test Driven Development – Video Shop
퀴즈 - VideoShop
Video & VideoCatalog
- VIDEO_TYPE enum 상수를 코드로 변경하는 것이 낫지 않을까요?
- VIDEO_TYPE enum은 Video 클래스 혹은 VideoCatalog 클래스 어느 쪽에 포함하는 게 나을까?
- Video 클래스를 상속해서, 스포츠, 다큐멘터리, 영화 클래스를 따로 생성하는 경우와 그렇지 않은 경우의 차이
는 무얼까요?
Video
+title
+type
+rent
+Video(title, type, rent)
+getTitle()
+getType()
+getRent()
VideoCatalog
+catalog
+register(title, type, rent)
+lookup()
16
Test Driven Development – Video Shop
퀴즈 - VideoShop
이제 좀 까다로운 문제입니다.
- 고객과 비디오를 등록하고 조회할 수 있으니까 다음 문제는...
- 고객이 비디오를 대여하는 프로세스를 테스트합니다.
- 가장 어려운 고비였습니다! 여러분은?
 대여는 누가 하나요?
- 당연히 고객이 대여하죠. 그래서 Customer 클래스에 choose() 메소드를 추가 했습니다.
 포인트를 적립해야 할 것 같습니다.
- 요금은 나중에 정산하지만, 포인트는 대여하는 시점에서 적립되겠죠.
- 그런데, 적립된 누적 포인트와 대여 중인 비디오를 통해서 얻은 포인트를 구분해야 합니다.
- 적립된 포인트를 어디선가 관리해야 하는 겁니다.
 그리고, 대여 중인 비디오 수를 알아야 합니다.
- 그러니까 대여 중인 비디오 내역을 어디선가 관리를 해야 하는거죠.
- 다행스럽게도 과거의 대역 내역은 조회할 필요가 없다고 합니다.
 계산은 언제?
- 태스크를 너무 크게 만들지 말라고 해서, 일단 대여하고, 대여 결과를 확인하는 것 까지로 한정 했습니다.
17
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 대여를 테스트 하기 위한 RentTest 클래스.
package test.acme.videoshop;
import java.util.List;
import com.acme.videoshop.counter.VideoRental;
import com.acme.videoshop.customer.Customer;
import com.acme.videoshop.customer.CustomerManager;
import com.acme.videoshop.util.DateUtils;
import com.acme.videoshop.video.Video;
import com.acme.videoshop.video.VideoCatalog;
import junit.framework.TestCase;
public class RentTest extends TestCase {
private static final String CUSTOMER_NAME = "Sunny Kwak";
public static String STAR_WARS = "StarWars";
public static String NBA_STARS = "NBA All Stars";
public static String THE_EARTH = "The Earth";
public void setUp() {
CustomerManager.register(CUSTOMER_NAME);
VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100);
VideoCatalog.register(THE_EARTH, VideoCatalog.VIDEO_TYPE.DOCUMENTARY, 200);
VideoCatalog.register(NBA_STARS, VideoCatalog.VIDEO_TYPE.SPORTS, 300);
}
(continued…)
18
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 대여를 테스트 하기 위한 RentTest 클래스.
public void testRent() {
Video movie, sports, documentary;
// 영화 비디오 대여 (어제)
Customer customer = CustomerManager.lookup(CUSTOMER_NAME);
movie = VideoCatalog.lookup(STAR_WARS);
assertNotNull(movie);
customer.choose(movie, DateUtils.minusDays(1));
// 적립 포인트 1, 발생 포인트 1, 대여 수 1
assertEquals(customer.getResevePoint(), 1);
assertEquals(customer.getIssuePoint(), 1);
assertEquals(customer.rentCount(), 1);
// 스포츠 비디오 대여 (그저께)
sports = VideoCatalog.lookup(NBA_STARS);
assertNotNull(sports);
customer.choose(sports, DateUtils.minusDays(2));
// 적립 포인트 3, 발생 포인트 2, 대여 수 2
assertEquals(customer.getResevePoint(), 3);
assertEquals(customer.getIssuePoint(), 3);
assertEquals(customer.rentCount(), 2);
(continued…)
19
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 대여를 테스트 하기 위한 RentTest 클래스.
// 다큐멘터리 비디오 대여 (오늘)
documentary = VideoCatalog.lookup(THE_EARTH);
assertNotNull(documentary);
customer.choose(documentary, DateUtils.getToday());
// 적립 포인트 4, 발생 포인트 4, 대여 수 3
assertEquals(customer.getResevePoint(), 4);
assertEquals(customer.getIssuePoint(), 4);
assertEquals(customer.rentCount(), 3);
// 대여정보: 비디오(종류 + 제목 + 가격), 대여기간 리스트
// int totalCharge = 0;
List<VideoRental> rentalList = customer.getRentalList();
for( VideoRental rental : rentalList ) {
Video video = rental.getVideo();
System.out.print( "Type : " + VideoCatalog.getTypeAsString(video.getType()) );
System.out.print( ", Title : " + video.getTitle() );
System.out.print( ", Rent : " + video.getRent() );
System.out.print( ", Rent Date : " + rental.getRentDate() );
System.out.println( ", Term : " + DateUtils.daysBetween(rental.getRentDate(),
DateUtils.getToday()) );
}
}
}
(end…)
20
Test Driven Development – Video Shop
퀴즈 - VideoShop
DateUtils, Customer, VideoRental & Video
현재 날짜, 며칠 전 날짜, 며칠 후 날짜 등을 얻기 위해 DateUtils 정적 클래스를 만들었습니다.
- 날짜는 DateTime 타입을 사용하는 방안도 있지만, 문자열로 관리하는 formatting 하기 편합니다.
- 기간 등을 계산하는 함수에서는 Joda Time API (http://joda-time.sourceforge.net)를 사용했습니다.
- 만일, 음력을 계산해야 하는 경우가 있다면, IBM ICU 오픈소스 라이브러리를 사용하시면 좋습니다.
적립 포인트 속성은 Customer 클래스 내부에 포함 시켰습니다.
비디오 대여 목록을 표현하기 위해서 VideoRental 클래스를 만들고, List 인터페이스를 이용해 Customer 클래스
내에 동적 배열로 선언했습니다.
DateUtils
+getToday()
+plusDays(int)
+minusDays(int)
Customer
+rentalList
+reservePoint
+choose()
+getIssuePoint()
+getReservePoint()
+rentCount()
+getRentalList()
VideoRental
+video
+rentDate
+getCharge()
+getPoint()
+getRentDate()
+getVideo()
Video
21
Test Driven Development – Video Shop
퀴즈 - VideoShop
Customer 클래스 : 비디오 대여
/**
* 비디오 대여.
*
* @param video 대여 비디오
* @param rentDate 대여 일자
*/
public void choose(Video video, String rentDate) {
prepareList();
VideoRental rental = new VideoRental(video, rentDate);
rentalList.add( rental );
reservePoint += rental.getPoint();
}
private void prepareList() {
if( rentalList == null ) {
rentalList = new ArrayList<VideoRental>();
}
}
22
Test Driven Development – Video Shop
퀴즈 - VideoShop
Customer 클래스 : 적립 포인트 반환 및 발생 포인트
/**
*
* @return 적립 포인트 반환
*/
public int getResevePoint() {
return reservePoint;
}
/**
*
* @return 현재 대여 중인 비디오로 인해 발생한 포인트
*/
public int getIssuePoint() {
int issuePoint = 0;
for( VideoRental rental : rentalList ) {
issuePoint += Counter.getPoint(rental.getVideo());
}
return issuePoint;
}
23
Test Driven Development – Video Shop
퀴즈 - VideoShop
VideoRental, Counter 클래스 : 비디오 별 포인트 반환
public class VideoRental {
public int getPoint() {
return Counter.getPoint(video);
}
}
public class Counter {
public static int getPoint(Video video) {
return video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ? 2 : 1;
}
}
24
Test Driven Development – Video Shop
퀴즈 - VideoShop
DateUtils, Customer, VideoRental & Video
- Customer 클래스 내부에 대여 내역과 적립 포인트를 포함시켰는데, 별도 클래스로 분리하는 건 어떨까요?
DateUtils
+getToday()
+plusDays(int)
+minusDays(int)
Customer
+rentalList
+reservePoint
+choose()
+getIssuePoint()
+getReservePoint()
+rentCount()
+getRentalList()
VideoRental
+video
+rentDate
+getCharge()
+getPoint()
+getRentDate()
+getVideo()
Video
25
Test Driven Development – Video Shop
퀴즈 - VideoShop
거의 마지막에 도착한 것 같습니다.
- 마지막 태스크는 고객이 비디오를 반납했을 때, 요금을 정산하는 프로세스입니다.
- 대충하느라 테스트 클래스를 따로 만들지 않았습니다.
 RentTest 클래스의 testRent() 메소드 변경
- 별도의 메소드를 작성하지 않고, testRent() 메소드에 코드를 추가했습니다.
 테스트는 꼼꼼하게…
- 검사 조건을 따져 봤습니다. 비디오 유형이 최소 3가지, 각각 할인해주는 경우와 할인 없는 경우가 있습니다.
- 게다가 오늘 오전에 빌리고, 오후에 반납하는 경우는 24시간을 넘지 않는데 이런 경우도 하루 요금을 받아야죠.
 그런데 반납하는 프로세스는 개발하지 않았습니다!
26
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.
public void testRent() {
// ---- 요금 계산을 해보자...! ----
// 스포츠는 장기대여 할인이 없다. (이게 제일 쉬우니 이것부터)
// 2일전 대여, 일일요금 300원
for( VideoRental rental : rentalList ) {
Video video = rental.getVideo();
if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ) {
// 오늘 반납 시, 2 * 300 = 600원
assertEquals( 600, rental.getCharge(DateUtils.getToday()));
// 내일 반납 시, 3 * 300 = 900원
assertEquals( 900, rental.getCharge(DateUtils.plusDays(1)));
}
}
// 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다.
// 어제 대여, 일일 요금 100원
for( VideoRental rental : rentalList ) {
Video video = rental.getVideo();
if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {
// 오늘 반납 시, 1 * 100 = 100원
assertEquals( 100, rental.getCharge(DateUtils.getToday()));
// 내일 반납 시, 2 * 100 = 200원
assertEquals( 200, rental.getCharge(DateUtils.plusDays(1)));
// 모레 반납 시, (2 * 100) + (1 * 100 / 2) = 250
assertEquals( 250, rental.getCharge(DateUtils.plusDays(2)));
}
}
(continued…)
27
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.
// 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다.
// 오늘 대여, 일일 요금 200원
for( VideoRental rental : rentalList ) {
Video video = rental.getVideo();
if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {
// 오늘 반납 시, 1 * 200 = 200원
assertEquals( 200, rental.getCharge(DateUtils.getToday()));
// 내일 반납 시, 1 * 200 = 200원
assertEquals( 200, rental.getCharge(DateUtils.plusDays(1)));
// 2일 후 반납 시, (2 * 200) = 400
assertEquals( 400, rental.getCharge(DateUtils.plusDays(2)));
// 4일 후 반납 시, (3 * 200) + (1 * 200 / 3) = 667
assertEquals( 666, rental.getCharge(DateUtils.plusDays(4)));
}
}
// ---- 요금 계산을끝내자...! ----
(continued…)
28
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.
// 전체 요금 계산 (4일 후 반납 시, 6일간 대여)
String restoreDate = DateUtils.plusDays(4);
int totalCharge = 0;
for( VideoRental rental : rentalList ) {
int eachCharge = rental.getCharge(DateUtils.plusDays(4));
Video video = rental.getVideo();
// 다큐멘터리 오늘 대여, 일일요금 200원
// 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667
if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {
assertEquals( eachCharge, 666 );
}
// 영화 어제 대여, 일일요금 100원
// 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350
else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {
assertEquals( eachCharge, 350 );
}
// 스포츠 2일전 대여, 일일요금 300원
// 6일 대여 요금 : 6 * 300 = 1800
else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {
assertEquals( eachCharge, 1800 );
}
totalCharge += eachCharge;
}
// 전체 요금 : 666 + 350 + 1800 = 2816
assertEquals(totalCharge, 2816);
(end…)
29
Test Driven Development – Video Shop
퀴즈 - VideoShop
비디오 요금 계산을 테스트 하기 위한 RentTest 클래스.
// 전체 요금 계산 (4일 후 반납 시, 6일간 대여)
String restoreDate = DateUtils.plusDays(4);
int totalCharge = 0;
for( VideoRental rental : rentalList ) {
int eachCharge = rental.getCharge(DateUtils.plusDays(4));
Video video = rental.getVideo();
// 다큐멘터리 오늘 대여, 일일요금 200원
// 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667
if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) {
assertEquals( eachCharge, 666 );
}
// 영화 어제 대여, 일일요금 100원
// 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350
else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) {
assertEquals( eachCharge, 350 );
}
// 스포츠 2일전 대여, 일일요금 300원
// 6일 대여 요금 : 6 * 300 = 1800
else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {
assertEquals( eachCharge, 1800 );
}
totalCharge += eachCharge;
}
// 전체 요금 : 666 + 350 + 1800 = 2816
assertEquals(totalCharge, 2816);
(end…)
30
Test Driven Development – Video Shop
퀴즈 - VideoShop
Counter 클래스
요금 및 포인트 계산을 Counter 클래스에 전담 시켰습니다.
기존의 경험 상 업무 규칙은 한 곳에 몰아 두는 것이 좋은 것 같습니다.
- 요금 할인/할증 규칙은 비디오 유형 별로 바뀌지 않고, 시기 (특정 계절에 따른 이벤트) 혹은 집합 조건 (2종 선택 시 하나 무료)
등 외적인 조건에 따라 바뀌는 경우가 많습니다.
DateUtils
+getToday()
+plusDays(int)
+minusDays(int)
Customer
+rentalList
+reservePoint
+choose()
+getIssuePoint()
+getReservePoint()
+rentCount()
+getRentalList()
VideoRental
+video
+rentDate
+getCharge()
+getPoint()
+getRentDate()
+getVideo()
Video
Counter
+getCharge()
+getPoint()
31
Test Driven Development – Video Shop
퀴즈 - VideoShop
VideoRental 클래스 : 요금 반환
public class VideoRental {
public int getCharge(String restoreDate) {
return Counter.getCharge(video, rentDate, restoreDate);
}
}
public static int getCharge(Video video, String rentDate, String restoreDate) {
int days = DateUtils.daysBetween(rentDate, restoreDate);
if (days == 0)
days = 1;
int charge = 0;
// 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다.
if (video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY) {
if( days < 4 ) {
charge = days * video.getRent();
}
else {
charge = (3 * video.getRent()) + ((days-3) * video.getRent() / 3);
}
}
(continued…)
32
Test Driven Development – Video Shop
퀴즈 - VideoShop
VideoRental 클래스 : 요금 반환
// 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다.
else if (video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE) {
if( days < 3 ) {
charge = days * video.getRent();
}
else {
charge = (2 * video.getRent()) + ((days-2) * video.getRent() / 2);
}
}
// 스포츠는 장기대여 할인이 없다.
else if (video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) {
charge = video.getRent() * days;
}
return charge;
}
(end…)
33
Test Driven Development – Video Shop
퀴즈 - VideoShop
DateUtils, Customer, VideoRental & Video
- 규칙이 변경 되는 상황일 발생하는 것을 예상한다면, Counter와 VideoRental 사이에 Factory 패턴을 적용하면 어떨까요?
DateUtils
+getToday()
+plusDays(int)
+minusDays(int)
Customer
+rentalList
+reservePoint
+choose()
+getIssuePoint()
+getReservePoint()
+rentCount()
+getRentalList()
VideoRental
+video
+rentDate
+getCharge()
+getPoint()
+getRentDate()
+getVideo()
Video
Counter
+getCharge()
+getPoint()
34
Test Driven Development – Video Shop
퀴즈 - VideoShop
Test Driven Development
[Video Shop]
봐주셔서 감사드립니다.
날카로운 지적 부탁 드립니다.

Contenu connexe

En vedette

프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정중선 곽
 
지속적인 통합
지속적인 통합지속적인 통합
지속적인 통합중선 곽
 
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)중선 곽
 
자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조중선 곽
 
젠킨스 설치 및 설정
젠킨스 설치 및 설정젠킨스 설치 및 설정
젠킨스 설치 및 설정중선 곽
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안중선 곽
 
Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)중선 곽
 
메이븐 기본 이해
메이븐 기본 이해메이븐 기본 이해
메이븐 기본 이해중선 곽
 
컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷중선 곽
 
Intranet query tuning (example)
Intranet query tuning (example)Intranet query tuning (example)
Intranet query tuning (example)중선 곽
 
Scale up and scale out
Scale up and scale outScale up and scale out
Scale up and scale out중선 곽
 
사칙연산 프로그램
사칙연산 프로그램사칙연산 프로그램
사칙연산 프로그램중선 곽
 
소프트웨어의 동작 방식 이해
소프트웨어의 동작 방식 이해소프트웨어의 동작 방식 이해
소프트웨어의 동작 방식 이해중선 곽
 
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리중선 곽
 
프로그래머가 알아야 하는 메모리 관리 기법
프로그래머가 알아야 하는 메모리 관리 기법프로그래머가 알아야 하는 메모리 관리 기법
프로그래머가 알아야 하는 메모리 관리 기법중선 곽
 
포스트모템디버깅과 프로세스 덤프 실전
포스트모템디버깅과 프로세스 덤프 실전포스트모템디버깅과 프로세스 덤프 실전
포스트모템디버깅과 프로세스 덤프 실전주항 박
 
[실전 윈도우 디버깅] 13 포스트모템 디버깅
[실전 윈도우 디버깅] 13 포스트모템 디버깅[실전 윈도우 디버깅] 13 포스트모템 디버깅
[실전 윈도우 디버깅] 13 포스트모템 디버깅종빈 오
 
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23우분투 한국 커뮤니티 나눔모임 발표 2013-02-23
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23유명환 FunFun Yoo
 
Tomcat monitoring using_javamelody
Tomcat monitoring using_javamelodyTomcat monitoring using_javamelody
Tomcat monitoring using_javamelody중선 곽
 

En vedette (20)

프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정프로그래밍 방식의 변천 과정
프로그래밍 방식의 변천 과정
 
지속적인 통합
지속적인 통합지속적인 통합
지속적인 통합
 
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
오픈소스 프레임워크 기반 웹 서비스 설계 (Example)
 
자바로 배우는 자료구조
자바로 배우는 자료구조자바로 배우는 자료구조
자바로 배우는 자료구조
 
젠킨스 설치 및 설정
젠킨스 설치 및 설정젠킨스 설치 및 설정
젠킨스 설치 및 설정
 
Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안Online service 계층별 성능 모니터링 방안
Online service 계층별 성능 모니터링 방안
 
Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)Db 진단 및 튜닝 보고 (example)
Db 진단 및 튜닝 보고 (example)
 
메이븐 기본 이해
메이븐 기본 이해메이븐 기본 이해
메이븐 기본 이해
 
컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷컴퓨터 네트워크와 인터넷
컴퓨터 네트워크와 인터넷
 
Intranet query tuning (example)
Intranet query tuning (example)Intranet query tuning (example)
Intranet query tuning (example)
 
Scale up and scale out
Scale up and scale outScale up and scale out
Scale up and scale out
 
사칙연산 프로그램
사칙연산 프로그램사칙연산 프로그램
사칙연산 프로그램
 
소프트웨어의 동작 방식 이해
소프트웨어의 동작 방식 이해소프트웨어의 동작 방식 이해
소프트웨어의 동작 방식 이해
 
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리
프로그래머가 알아야 하는 2진수 기반의 컴퓨터 동작 원리
 
프로그래머가 알아야 하는 메모리 관리 기법
프로그래머가 알아야 하는 메모리 관리 기법프로그래머가 알아야 하는 메모리 관리 기법
프로그래머가 알아야 하는 메모리 관리 기법
 
포스트모템디버깅과 프로세스 덤프 실전
포스트모템디버깅과 프로세스 덤프 실전포스트모템디버깅과 프로세스 덤프 실전
포스트모템디버깅과 프로세스 덤프 실전
 
[실전 윈도우 디버깅] 13 포스트모템 디버깅
[실전 윈도우 디버깅] 13 포스트모템 디버깅[실전 윈도우 디버깅] 13 포스트모템 디버깅
[실전 윈도우 디버깅] 13 포스트모템 디버깅
 
Maven의 이해
Maven의 이해Maven의 이해
Maven의 이해
 
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23우분투 한국 커뮤니티 나눔모임 발표 2013-02-23
우분투 한국 커뮤니티 나눔모임 발표 2013-02-23
 
Tomcat monitoring using_javamelody
Tomcat monitoring using_javamelodyTomcat monitoring using_javamelody
Tomcat monitoring using_javamelody
 

Similaire à Test driven development short lesson

Legacy code refactoring video rental system
Legacy code refactoring   video rental systemLegacy code refactoring   video rental system
Legacy code refactoring video rental systemJaehoon Oh
 
X unittestpattern 1장_아꿈사
X unittestpattern 1장_아꿈사X unittestpattern 1장_아꿈사
X unittestpattern 1장_아꿈사효원 강
 
[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridappNAVER D2
 
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022Amazon Web Services Korea
 
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드SangIn Choung
 
Html5 canvas6 week6n7n8
Html5 canvas6 week6n7n8Html5 canvas6 week6n7n8
Html5 canvas6 week6n7n8Juneyoung Oh
 
TDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDTDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDSuwon Chae
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018Kenneth Ceyer
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Testbeom kyun choi
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 SangIn Choung
 
테스트자동화 성공전략
테스트자동화 성공전략테스트자동화 성공전략
테스트자동화 성공전략SangIn Choung
 
테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션SangIn Choung
 
시작하자 단위테스트
시작하자 단위테스트시작하자 단위테스트
시작하자 단위테스트YongEun Choi
 
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2tobeware
 
Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발지수 윤
 
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4Amazon Web Services Korea
 
우리 제품의 검증 프로세스 소개 자료
우리 제품의 검증 프로세스 소개 자료 우리 제품의 검증 프로세스 소개 자료
우리 제품의 검증 프로세스 소개 자료 SangIn Choung
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 

Similaire à Test driven development short lesson (20)

Legacy code refactoring video rental system
Legacy code refactoring   video rental systemLegacy code refactoring   video rental system
Legacy code refactoring video rental system
 
X unittestpattern 1장_아꿈사
X unittestpattern 1장_아꿈사X unittestpattern 1장_아꿈사
X unittestpattern 1장_아꿈사
 
[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp[D2 오픈세미나]3.web view hybridapp
[D2 오픈세미나]3.web view hybridapp
 
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022
AWS 머신러닝 솔루션을 활용한 고객 응대 자동화 구축 사례 공유 - 이창명, CTO, 위메이드 플레이 ::: Games on AWS 2022
 
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
katalon studio 툴을 이용한 GUI 테스트 자동화 가이드
 
Html5 canvas6 week6n7n8
Html5 canvas6 week6n7n8Html5 canvas6 week6n7n8
Html5 canvas6 week6n7n8
 
TDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDDTDD&Refactoring Day 02: TDD
TDD&Refactoring Day 02: TDD
 
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
우아하게 준비하는 테스트와 리팩토링 - PyCon Korea 2018
 
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 TestOkjsp 13주년 발표자료: 생존 프로그래밍 Test
Okjsp 13주년 발표자료: 생존 프로그래밍 Test
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
 
테스트자동화 성공전략
테스트자동화 성공전략테스트자동화 성공전략
테스트자동화 성공전략
 
테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션테스트수행사례 W통합보안솔루션
테스트수행사례 W통합보안솔루션
 
시작하자 단위테스트
시작하자 단위테스트시작하자 단위테스트
시작하자 단위테스트
 
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
투비웨어 AgitarOne Junit 단위테스트자동화 솔루션소개_201608_v1.2
 
Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발Jenkins를 활용한 javascript 개발
Jenkins를 활용한 javascript 개발
 
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
AWS로 게임 런칭 준비하기 ::: 장준성, 채민관, AWS Game Master 온라인 시리즈 #4
 
우리 제품의 검증 프로세스 소개 자료
우리 제품의 검증 프로세스 소개 자료 우리 제품의 검증 프로세스 소개 자료
우리 제품의 검증 프로세스 소개 자료
 
Tdd with JUnit 1
Tdd with JUnit 1Tdd with JUnit 1
Tdd with JUnit 1
 
Cygnus unit test
Cygnus unit testCygnus unit test
Cygnus unit test
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 

Plus de 중선 곽

Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2중선 곽
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드중선 곽
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드중선 곽
 
자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)중선 곽
 
숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)중선 곽
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문중선 곽
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문중선 곽
 
서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해중선 곽
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개중선 곽
 
객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념중선 곽
 
Effective java 1 and 2
Effective java 1 and 2Effective java 1 and 2
Effective java 1 and 2중선 곽
 
지식경영 이해
지식경영 이해지식경영 이해
지식경영 이해중선 곽
 
Continue break goto_에_대한_고찰
Continue break goto_에_대한_고찰Continue break goto_에_대한_고찰
Continue break goto_에_대한_고찰중선 곽
 
폰노이만 머신 이해
폰노이만 머신 이해폰노이만 머신 이해
폰노이만 머신 이해중선 곽
 

Plus de 중선 곽 (14)

Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2Web service performance_test_using_jmeter_ver1.2
Web service performance_test_using_jmeter_ver1.2
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드
 
Java rmi 개발 가이드
Java rmi 개발 가이드Java rmi 개발 가이드
Java rmi 개발 가이드
 
자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)자바 직렬화 (Java serialization)
자바 직렬화 (Java serialization)
 
숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)숫자 구분자 처리 (Digit group separators)
숫자 구분자 처리 (Digit group separators)
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문
 
서버 아키텍쳐 입문
서버 아키텍쳐 입문서버 아키텍쳐 입문
서버 아키텍쳐 입문
 
서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해서버 성능에 대한 정의와 이해
서버 성능에 대한 정의와 이해
 
Apache ZooKeeper 소개
Apache ZooKeeper 소개Apache ZooKeeper 소개
Apache ZooKeeper 소개
 
객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념객체지향 철학 그리고 5대 개념
객체지향 철학 그리고 5대 개념
 
Effective java 1 and 2
Effective java 1 and 2Effective java 1 and 2
Effective java 1 and 2
 
지식경영 이해
지식경영 이해지식경영 이해
지식경영 이해
 
Continue break goto_에_대한_고찰
Continue break goto_에_대한_고찰Continue break goto_에_대한_고찰
Continue break goto_에_대한_고찰
 
폰노이만 머신 이해
폰노이만 머신 이해폰노이만 머신 이해
폰노이만 머신 이해
 

Test driven development short lesson

  • 1. This report is written by Sunny Kwak. (sunnykwak@hanmail.net, sunnykwak.tistory.com) 2016.03.07 Test Driven Development (mini-lesson) Version 0.2
  • 2. 1 Test Driven Development – Video Shop Test Driven Development 접근하기 Test Driven Development (살살) 접근하기
  • 3. 2 Test Driven Development – Video Shop Test Driven Development 접근하기  프로그래밍은 주어진 과제를 해결하는 것이다.  최초 과제 혹은 요구사항은 대체로 단순하다.  하지만, 점점 요구가 복잡해진다. (간혹 처음부터 조건이 까다로운 경우도…)  일단 쏴서 맞추어 봐라. 그래서 시키는 대로 했다.  점점 앉아쏴, 서서 쏴, 업드려 쏴… 설마 덤블링 하면서 쏘라고 하나? 목표를 쏴라~
  • 4. 3 Test Driven Development – Video Shop Test Driven Development 접근하기 Non TDD 문제를 더 작은 문제로 쪼갠다. 각각의 작은 문제를 해결하기 위한 모듈(객체)들과 각 모듈간의 관계(상속, 합성)를 정의한다. 그리고, 문제를 해결하기 위한 코드를 작성한다. (원칙은 그렇다고 한다.) 문제를 더 작은 문제로 쪼갠다. 하나의 컴포넌트를 선언하고, 작은 문제 하나를 푼다. 그리고, 또 다른 문제를 해결하기 위해 기존 컴포넌트에 코드를 계속 추가한다. 필요한 기능을 추가할 수록 점점 스파게티 코드가 되어간다. 리팩토링을 해서 코드를 개선하기도 하지만… 테스트가 점점 어려워진다. 하나의 컴포넌트가 여러가지 이상의 기능을 수행하면서 최초에 테스트 했던 기능이 동작하지 않는다. 그리고, 점점 억울해진다. “아니, 이거 원래 잘 동작하던 건데요? 미치겠네..” 선배도 답답하고, 고객은 열받고, 개발자는 집에 가고 싶을 뿐이고~ 사실은?
  • 5. 4 Test Driven Development – Video Shop Test Driven Development 접근하기 Test Driven Development! 작은 목표 혹은 문제를 선정한다. 문제에 대한 답을 예상한다. 그리고, 예상 결과를 검사하는 코드를 먼저 작성한다. 결과를 반환하는 컴포넌트를 선언하고, 간단히 결과를 생성하는 실행 코드를 작성한다. 위 과정을 반복하면서 필요하다면, 리팩토링을 하며 컴포넌트를 추가하거나 구조를 변경한다. 그래서? 요구사항 혹은 조건이 추가되거나, 기능을 추가했을 때 이전에 동작하던 기능이 잘 동작하는지 테스트 케이스가 자동으로 확인해 준다. 대체로 잘 동작하는 상태로 업무가 진행된다. 자동화된 테스트를 통해 문제 여부를 빠르게 확인할 수 있어서 테스트 시간과 심리적 부담이 감소한다. 늘 동작하는 코드를 보면서 작업하게 되니. 한참 만들어 놓고, 동작 안하는 코드를 보고 절망할 일이 없다. 코드 분량이 늘어나도 적극적으로 코드를 수정할 수 있게 된다. 정시 퇴근이 (어쩌면) 가능할지도 모른다. 문제의 범위를 크게 잡지 말고, 조금씩 기능을 개선하며 리듬을 타야 한다.
  • 6. 5 Test Driven Development – Video Shop 퀴즈 - VideoShop Test Driven Development 퀴즈 풀기
  • 7. 6 Test Driven Development – Video Shop 퀴즈 - VideoShop 고객 비디오테이프 대여 서비스 Video Shop  고객은 이름을 가지고 있다.  고객은 한번에 여러 개의 비디오를 대여할 수 있고, 각각의 대여 기간은 일정하지 않다.  비디오는 3가지 유형이 있다. (영화, 스포츠, 다큐멘터리)  각각의 비디오는 독립적인 일일 대여요금이 부여되며, 유형에 따라 할인율이 틀리다.  비디오 대여 시 유형에 따라 다른 포인트를 제공한다.  비디오 대여가 발생할 때, 포인트를 적립하고 누적 포인트를 조회할 수 있어야 한다.  상세 대여 내역 (종류, 제목, 가격 등)을 조회할 수 있어야 한다.  대여 기간에 따라 요금을 계산할 수 있어야 한다. 기타 조건  TDD의 순서를 따라서 개발할 것.  테스트할 목록을 작성하라.
  • 8. 7 Test Driven Development – Video Shop 퀴즈 - VideoShop 문제가 너무 커 보이는데요? 어디서 부터 접근해야 할까요? 문제를 먼저 쪼개는 작업이 필요하지 않나요? 자칫 엉뚱한 곳부터 풀다가 헤매는 건 아닐까요? 저는 TDD를 처음 경험해 보는데 조언 해주실 건 없나요? 어디서 시작하나 상관 없는 게 TDD 입니다. 두려워하지 마시고 일단 시작해 보세요! Just Do IT!
  • 9. 8 Test Driven Development – Video Shop 퀴즈 - VideoShop 그래서, 그냥 순서대로 풀었습니다! - 첫번째 문제 혹은 태스크 ‘고객은 이름을 가지고 있다.’ - 이게 전부는 아니죠… 새로운 고객을 등록하고, 이름으로 검색할 수 있어야 합니다. - 그래서, 아래와 같이 작업했습니다.  개발 환경 - 이클립스를 실행한 후 워크스페이스를 만들고, VideoShop 프로젝트를 생성했습니다  패키지 선언 - 테스트 코드와 실행 코드는 다른 패키지에 위치 시켜야 한답니다. - 그래서, com.acme.videoshop 패키지와 test.acme.video.shop 으로 분리했습니다.  테스트 프레임워크 설정 - 다양한 도구가 있지만, 이클립스에 기본으로 포함되어 있는 Junit을 선택했습니다.  테스트 클래스 선언 - test.acme.videoshop. CustomerTest 클래스를 작성합니다.
  • 10. 9 Test Driven Development – Video Shop 퀴즈 - VideoShop 고객 관리를 테스트 하기 위한 CustomerTest 클래스 작성. package test.acme.videoshop; import com.acme.videoshop.customer.Customer; import com.acme.videoshop.customer.CustomerManager; import junit.framework.TestCase; public class CustomerTest extends TestCase { private static final String CUSTOMER_NAME = "Sunny Kwak"; public void testCustomer() { Customer customer = CustomerManager.register( CUSTOMER_NAME ); assertNotNull(customer); customer = CustomerManager.lookup(CUSTOMER_NAME); assertNotNull(customer); customer = CustomerManager.lookup(CUSTOMER_NAME + "."); assertNull(customer); } }
  • 11. 10 Test Driven Development – Video Shop 퀴즈 - VideoShop Customer & CustomerManager Customer +name +Customer(String name) +getName() CustomerManager +customerMap +register(String customerName) +lookup(String customerName) 고객 객체를 직접 생성하지 않고, CustomerManager 클래스의 register() 메소드를 통해 간접 생성. 고객의 이름 검색 또한 lookup() 메소드를 통해 수행. - 고객 관리에 대한 모든 책임을 CustomerManager에 위임. - 고객 정보를 저장소 등으로 보내거나, 읽어오게끔 변경하더라도 CustomerManager 외부에서는 변경 사항을 알아차리지 못하게 됨. - 이를 통해 정보 은폐(information hiding)을 시도함.
  • 12. 11 Test Driven Development – Video Shop 퀴즈 - VideoShop Customer & CustomerManager Customer +name +Customer(String name) +getName() CustomerManager +customerMap +register(String customerName) +lookup(String customerName) - Customer 객체를 CustomerManager의 register() 메소드 내부에서 생성하고 있습니다. - 고객에 대한 정보(항목)가 늘어날 수도 있으니 Customer 객체를 생성한 후에 register() 메소드에 Customer 객체를 파라미터로 전달하는 건 어떨까요? - 만일 구조를 바꾸게 된다면, CustomerManager가 Customer 객체를 생성하는 경우와 그렇지 않을 경우 장단 점은 무엇일까요? 확실한 답을 얻기 위해 질문하는 것이 아닙니다. 어차피 정답이 없을 수도 없지만, 생각하는 습관을 들이고, 남의 의견을 경청하기 위한 훈련입니다. 멍청한 질문이 되지 않을까 두려워 하면 아무 것도 배우지 못합니다!
  • 13. 12 Test Driven Development – Video Shop 퀴즈 - VideoShop 그리고, 두번째 태스크를 선택 했습니다. - 내용은 거의 유사합니다. ‘비디오 상품 관리’ - 비디오 유형에 따라 분리해 등록하고, 제목으로 검색할 수 있어야 합니다. - 비디오 테이프마다 대여료가 다르다고 하는군요.  테스트 케이스 클래스 생성 - VideoTapeTest 클래스를 작성합니다.  테스트 메소드 생성 - testVideo() 메소드를 작성했습니다. - 비디오 테이프를 등록하고, 정상적으로 등록되었는지 등록된 내용을 검사합니다. - 비디오 테이프를 등록 한후 타이틀로 검색하고, 존재하는지 검사합니다.  실행 코드 작성 - VideoCatalog, Video 클래스를 생성합니다.
  • 14. 13 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 목록 관리를 테스트 하기 위한 VideoTest 클래스. package test.acme.videoshop; import com.acme.videoshop.video.Video; import com.acme.videoshop.video.VideoCatalog; import junit.framework.TestCase; public class VideoTapeTest extends TestCase { public static String STAR_WARS = "StarWars"; public void testVideo() { Video item = VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100); assertNotNull(item); assertEquals(item.getTitle(), STAR_WARS); assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE); assertEquals(item.getRent(), 100); item = VideoCatalog.lookup(STAR_WARS); assertNotNull(item); assertEquals(item.getTitle(), STAR_WARS); assertEquals(item.getType(), VideoCatalog.VIDEO_TYPE.MOVIE); assertEquals(item.getRent(), 100); item = VideoCatalog.lookup(STAR_WARS + "."); assertNull(item); } }
  • 15. 14 Test Driven Development – Video Shop 퀴즈 - VideoShop Video & VideoCatalog 비디오 객체를 직접 생성하지 않고, VideoCatalog 클래스의 register() 메소드를 통해 간접 생성. 비디오 검색 또한 lookup() 메소드를 통해 수행. -비디오 유형을 표현하기 위해서 상속하는 방법이 있지만, 불필요한 클래스 추가라고 판단되어 enum 선언 public enum VIDEO_TYPE { MOVIE, SPORTS, DOCUMENTARY }; Video +title +type +rent +Video(title, type, rent) +getTitle() +getType() +getRent() VideoCatalog +catalog +register(title, type, rent) +lookup()
  • 16. 15 Test Driven Development – Video Shop 퀴즈 - VideoShop Video & VideoCatalog - VIDEO_TYPE enum 상수를 코드로 변경하는 것이 낫지 않을까요? - VIDEO_TYPE enum은 Video 클래스 혹은 VideoCatalog 클래스 어느 쪽에 포함하는 게 나을까? - Video 클래스를 상속해서, 스포츠, 다큐멘터리, 영화 클래스를 따로 생성하는 경우와 그렇지 않은 경우의 차이 는 무얼까요? Video +title +type +rent +Video(title, type, rent) +getTitle() +getType() +getRent() VideoCatalog +catalog +register(title, type, rent) +lookup()
  • 17. 16 Test Driven Development – Video Shop 퀴즈 - VideoShop 이제 좀 까다로운 문제입니다. - 고객과 비디오를 등록하고 조회할 수 있으니까 다음 문제는... - 고객이 비디오를 대여하는 프로세스를 테스트합니다. - 가장 어려운 고비였습니다! 여러분은?  대여는 누가 하나요? - 당연히 고객이 대여하죠. 그래서 Customer 클래스에 choose() 메소드를 추가 했습니다.  포인트를 적립해야 할 것 같습니다. - 요금은 나중에 정산하지만, 포인트는 대여하는 시점에서 적립되겠죠. - 그런데, 적립된 누적 포인트와 대여 중인 비디오를 통해서 얻은 포인트를 구분해야 합니다. - 적립된 포인트를 어디선가 관리해야 하는 겁니다.  그리고, 대여 중인 비디오 수를 알아야 합니다. - 그러니까 대여 중인 비디오 내역을 어디선가 관리를 해야 하는거죠. - 다행스럽게도 과거의 대역 내역은 조회할 필요가 없다고 합니다.  계산은 언제? - 태스크를 너무 크게 만들지 말라고 해서, 일단 대여하고, 대여 결과를 확인하는 것 까지로 한정 했습니다.
  • 18. 17 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 대여를 테스트 하기 위한 RentTest 클래스. package test.acme.videoshop; import java.util.List; import com.acme.videoshop.counter.VideoRental; import com.acme.videoshop.customer.Customer; import com.acme.videoshop.customer.CustomerManager; import com.acme.videoshop.util.DateUtils; import com.acme.videoshop.video.Video; import com.acme.videoshop.video.VideoCatalog; import junit.framework.TestCase; public class RentTest extends TestCase { private static final String CUSTOMER_NAME = "Sunny Kwak"; public static String STAR_WARS = "StarWars"; public static String NBA_STARS = "NBA All Stars"; public static String THE_EARTH = "The Earth"; public void setUp() { CustomerManager.register(CUSTOMER_NAME); VideoCatalog.register(STAR_WARS, VideoCatalog.VIDEO_TYPE.MOVIE, 100); VideoCatalog.register(THE_EARTH, VideoCatalog.VIDEO_TYPE.DOCUMENTARY, 200); VideoCatalog.register(NBA_STARS, VideoCatalog.VIDEO_TYPE.SPORTS, 300); } (continued…)
  • 19. 18 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 대여를 테스트 하기 위한 RentTest 클래스. public void testRent() { Video movie, sports, documentary; // 영화 비디오 대여 (어제) Customer customer = CustomerManager.lookup(CUSTOMER_NAME); movie = VideoCatalog.lookup(STAR_WARS); assertNotNull(movie); customer.choose(movie, DateUtils.minusDays(1)); // 적립 포인트 1, 발생 포인트 1, 대여 수 1 assertEquals(customer.getResevePoint(), 1); assertEquals(customer.getIssuePoint(), 1); assertEquals(customer.rentCount(), 1); // 스포츠 비디오 대여 (그저께) sports = VideoCatalog.lookup(NBA_STARS); assertNotNull(sports); customer.choose(sports, DateUtils.minusDays(2)); // 적립 포인트 3, 발생 포인트 2, 대여 수 2 assertEquals(customer.getResevePoint(), 3); assertEquals(customer.getIssuePoint(), 3); assertEquals(customer.rentCount(), 2); (continued…)
  • 20. 19 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 대여를 테스트 하기 위한 RentTest 클래스. // 다큐멘터리 비디오 대여 (오늘) documentary = VideoCatalog.lookup(THE_EARTH); assertNotNull(documentary); customer.choose(documentary, DateUtils.getToday()); // 적립 포인트 4, 발생 포인트 4, 대여 수 3 assertEquals(customer.getResevePoint(), 4); assertEquals(customer.getIssuePoint(), 4); assertEquals(customer.rentCount(), 3); // 대여정보: 비디오(종류 + 제목 + 가격), 대여기간 리스트 // int totalCharge = 0; List<VideoRental> rentalList = customer.getRentalList(); for( VideoRental rental : rentalList ) { Video video = rental.getVideo(); System.out.print( "Type : " + VideoCatalog.getTypeAsString(video.getType()) ); System.out.print( ", Title : " + video.getTitle() ); System.out.print( ", Rent : " + video.getRent() ); System.out.print( ", Rent Date : " + rental.getRentDate() ); System.out.println( ", Term : " + DateUtils.daysBetween(rental.getRentDate(), DateUtils.getToday()) ); } } } (end…)
  • 21. 20 Test Driven Development – Video Shop 퀴즈 - VideoShop DateUtils, Customer, VideoRental & Video 현재 날짜, 며칠 전 날짜, 며칠 후 날짜 등을 얻기 위해 DateUtils 정적 클래스를 만들었습니다. - 날짜는 DateTime 타입을 사용하는 방안도 있지만, 문자열로 관리하는 formatting 하기 편합니다. - 기간 등을 계산하는 함수에서는 Joda Time API (http://joda-time.sourceforge.net)를 사용했습니다. - 만일, 음력을 계산해야 하는 경우가 있다면, IBM ICU 오픈소스 라이브러리를 사용하시면 좋습니다. 적립 포인트 속성은 Customer 클래스 내부에 포함 시켰습니다. 비디오 대여 목록을 표현하기 위해서 VideoRental 클래스를 만들고, List 인터페이스를 이용해 Customer 클래스 내에 동적 배열로 선언했습니다. DateUtils +getToday() +plusDays(int) +minusDays(int) Customer +rentalList +reservePoint +choose() +getIssuePoint() +getReservePoint() +rentCount() +getRentalList() VideoRental +video +rentDate +getCharge() +getPoint() +getRentDate() +getVideo() Video
  • 22. 21 Test Driven Development – Video Shop 퀴즈 - VideoShop Customer 클래스 : 비디오 대여 /** * 비디오 대여. * * @param video 대여 비디오 * @param rentDate 대여 일자 */ public void choose(Video video, String rentDate) { prepareList(); VideoRental rental = new VideoRental(video, rentDate); rentalList.add( rental ); reservePoint += rental.getPoint(); } private void prepareList() { if( rentalList == null ) { rentalList = new ArrayList<VideoRental>(); } }
  • 23. 22 Test Driven Development – Video Shop 퀴즈 - VideoShop Customer 클래스 : 적립 포인트 반환 및 발생 포인트 /** * * @return 적립 포인트 반환 */ public int getResevePoint() { return reservePoint; } /** * * @return 현재 대여 중인 비디오로 인해 발생한 포인트 */ public int getIssuePoint() { int issuePoint = 0; for( VideoRental rental : rentalList ) { issuePoint += Counter.getPoint(rental.getVideo()); } return issuePoint; }
  • 24. 23 Test Driven Development – Video Shop 퀴즈 - VideoShop VideoRental, Counter 클래스 : 비디오 별 포인트 반환 public class VideoRental { public int getPoint() { return Counter.getPoint(video); } } public class Counter { public static int getPoint(Video video) { return video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ? 2 : 1; } }
  • 25. 24 Test Driven Development – Video Shop 퀴즈 - VideoShop DateUtils, Customer, VideoRental & Video - Customer 클래스 내부에 대여 내역과 적립 포인트를 포함시켰는데, 별도 클래스로 분리하는 건 어떨까요? DateUtils +getToday() +plusDays(int) +minusDays(int) Customer +rentalList +reservePoint +choose() +getIssuePoint() +getReservePoint() +rentCount() +getRentalList() VideoRental +video +rentDate +getCharge() +getPoint() +getRentDate() +getVideo() Video
  • 26. 25 Test Driven Development – Video Shop 퀴즈 - VideoShop 거의 마지막에 도착한 것 같습니다. - 마지막 태스크는 고객이 비디오를 반납했을 때, 요금을 정산하는 프로세스입니다. - 대충하느라 테스트 클래스를 따로 만들지 않았습니다.  RentTest 클래스의 testRent() 메소드 변경 - 별도의 메소드를 작성하지 않고, testRent() 메소드에 코드를 추가했습니다.  테스트는 꼼꼼하게… - 검사 조건을 따져 봤습니다. 비디오 유형이 최소 3가지, 각각 할인해주는 경우와 할인 없는 경우가 있습니다. - 게다가 오늘 오전에 빌리고, 오후에 반납하는 경우는 24시간을 넘지 않는데 이런 경우도 하루 요금을 받아야죠.  그런데 반납하는 프로세스는 개발하지 않았습니다!
  • 27. 26 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 요금 계산을 테스트 하기 위한 RentTest 클래스. public void testRent() { // ---- 요금 계산을 해보자...! ---- // 스포츠는 장기대여 할인이 없다. (이게 제일 쉬우니 이것부터) // 2일전 대여, 일일요금 300원 for( VideoRental rental : rentalList ) { Video video = rental.getVideo(); if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS ) { // 오늘 반납 시, 2 * 300 = 600원 assertEquals( 600, rental.getCharge(DateUtils.getToday())); // 내일 반납 시, 3 * 300 = 900원 assertEquals( 900, rental.getCharge(DateUtils.plusDays(1))); } } // 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다. // 어제 대여, 일일 요금 100원 for( VideoRental rental : rentalList ) { Video video = rental.getVideo(); if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) { // 오늘 반납 시, 1 * 100 = 100원 assertEquals( 100, rental.getCharge(DateUtils.getToday())); // 내일 반납 시, 2 * 100 = 200원 assertEquals( 200, rental.getCharge(DateUtils.plusDays(1))); // 모레 반납 시, (2 * 100) + (1 * 100 / 2) = 250 assertEquals( 250, rental.getCharge(DateUtils.plusDays(2))); } } (continued…)
  • 28. 27 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 요금 계산을 테스트 하기 위한 RentTest 클래스. // 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다. // 오늘 대여, 일일 요금 200원 for( VideoRental rental : rentalList ) { Video video = rental.getVideo(); if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) { // 오늘 반납 시, 1 * 200 = 200원 assertEquals( 200, rental.getCharge(DateUtils.getToday())); // 내일 반납 시, 1 * 200 = 200원 assertEquals( 200, rental.getCharge(DateUtils.plusDays(1))); // 2일 후 반납 시, (2 * 200) = 400 assertEquals( 400, rental.getCharge(DateUtils.plusDays(2))); // 4일 후 반납 시, (3 * 200) + (1 * 200 / 3) = 667 assertEquals( 666, rental.getCharge(DateUtils.plusDays(4))); } } // ---- 요금 계산을끝내자...! ---- (continued…)
  • 29. 28 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 요금 계산을 테스트 하기 위한 RentTest 클래스. // 전체 요금 계산 (4일 후 반납 시, 6일간 대여) String restoreDate = DateUtils.plusDays(4); int totalCharge = 0; for( VideoRental rental : rentalList ) { int eachCharge = rental.getCharge(DateUtils.plusDays(4)); Video video = rental.getVideo(); // 다큐멘터리 오늘 대여, 일일요금 200원 // 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667 if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) { assertEquals( eachCharge, 666 ); } // 영화 어제 대여, 일일요금 100원 // 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350 else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) { assertEquals( eachCharge, 350 ); } // 스포츠 2일전 대여, 일일요금 300원 // 6일 대여 요금 : 6 * 300 = 1800 else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) { assertEquals( eachCharge, 1800 ); } totalCharge += eachCharge; } // 전체 요금 : 666 + 350 + 1800 = 2816 assertEquals(totalCharge, 2816); (end…)
  • 30. 29 Test Driven Development – Video Shop 퀴즈 - VideoShop 비디오 요금 계산을 테스트 하기 위한 RentTest 클래스. // 전체 요금 계산 (4일 후 반납 시, 6일간 대여) String restoreDate = DateUtils.plusDays(4); int totalCharge = 0; for( VideoRental rental : rentalList ) { int eachCharge = rental.getCharge(DateUtils.plusDays(4)); Video video = rental.getVideo(); // 다큐멘터리 오늘 대여, 일일요금 200원 // 4일 대여 요금 : (3 * 200) + (1 * 200 / 3) = 667 if( video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY ) { assertEquals( eachCharge, 666 ); } // 영화 어제 대여, 일일요금 100원 // 5일 대여 요금 : (2 * 100) + (3 * 100 / 2) = 350 else if( video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE ) { assertEquals( eachCharge, 350 ); } // 스포츠 2일전 대여, 일일요금 300원 // 6일 대여 요금 : 6 * 300 = 1800 else if( video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) { assertEquals( eachCharge, 1800 ); } totalCharge += eachCharge; } // 전체 요금 : 666 + 350 + 1800 = 2816 assertEquals(totalCharge, 2816); (end…)
  • 31. 30 Test Driven Development – Video Shop 퀴즈 - VideoShop Counter 클래스 요금 및 포인트 계산을 Counter 클래스에 전담 시켰습니다. 기존의 경험 상 업무 규칙은 한 곳에 몰아 두는 것이 좋은 것 같습니다. - 요금 할인/할증 규칙은 비디오 유형 별로 바뀌지 않고, 시기 (특정 계절에 따른 이벤트) 혹은 집합 조건 (2종 선택 시 하나 무료) 등 외적인 조건에 따라 바뀌는 경우가 많습니다. DateUtils +getToday() +plusDays(int) +minusDays(int) Customer +rentalList +reservePoint +choose() +getIssuePoint() +getReservePoint() +rentCount() +getRentalList() VideoRental +video +rentDate +getCharge() +getPoint() +getRentDate() +getVideo() Video Counter +getCharge() +getPoint()
  • 32. 31 Test Driven Development – Video Shop 퀴즈 - VideoShop VideoRental 클래스 : 요금 반환 public class VideoRental { public int getCharge(String restoreDate) { return Counter.getCharge(video, rentDate, restoreDate); } } public static int getCharge(Video video, String rentDate, String restoreDate) { int days = DateUtils.daysBetween(rentDate, restoreDate); if (days == 0) days = 1; int charge = 0; // 다큐멘타리는 3일 이상 대여하면 4일째 부터는 1/3로 할인된다. if (video.getType() == VideoCatalog.VIDEO_TYPE.DOCUMENTARY) { if( days < 4 ) { charge = days * video.getRent(); } else { charge = (3 * video.getRent()) + ((days-3) * video.getRent() / 3); } } (continued…)
  • 33. 32 Test Driven Development – Video Shop 퀴즈 - VideoShop VideoRental 클래스 : 요금 반환 // 영화는 대여기간이 2일 이상되면 3일째 부터는 대여요금이 1/2로 할인된다. else if (video.getType() == VideoCatalog.VIDEO_TYPE.MOVIE) { if( days < 3 ) { charge = days * video.getRent(); } else { charge = (2 * video.getRent()) + ((days-2) * video.getRent() / 2); } } // 스포츠는 장기대여 할인이 없다. else if (video.getType() == VideoCatalog.VIDEO_TYPE.SPORTS) { charge = video.getRent() * days; } return charge; } (end…)
  • 34. 33 Test Driven Development – Video Shop 퀴즈 - VideoShop DateUtils, Customer, VideoRental & Video - 규칙이 변경 되는 상황일 발생하는 것을 예상한다면, Counter와 VideoRental 사이에 Factory 패턴을 적용하면 어떨까요? DateUtils +getToday() +plusDays(int) +minusDays(int) Customer +rentalList +reservePoint +choose() +getIssuePoint() +getReservePoint() +rentCount() +getRentalList() VideoRental +video +rentDate +getCharge() +getPoint() +getRentDate() +getVideo() Video Counter +getCharge() +getPoint()
  • 35. 34 Test Driven Development – Video Shop 퀴즈 - VideoShop Test Driven Development [Video Shop] 봐주셔서 감사드립니다. 날카로운 지적 부탁 드립니다.