Notice
Recent Posts
Recent Comments
Dharma
[TDD] 책에서 퍼온 피보나치 구현 본문
테스트 주도 개발 책을 읽으면서 그 책에서 제일 괜찮다고 생각하는
부분중에 한 부분입니다. TDD 개념을 가장 쉽게 접근할 수 있도록 만든
예제인데 무단으로 퍼서 소개할까 합니다. - 사실 본 책 안에 있는 내용은
좀 난해한 편이고 , 이 예제가 더 이해하기 쉽습니다. -
제가 어디서 본 이야기인데 , TDD 를 단지 단위테스트 용 기법으로
생각하시는 분들이 많다고 합니다. 그러나 TDD는 '분석기법이고,
설계기법' 입니다. 그런것을 염두에 두시고 다시 한번 피보나치 구현을
보시면 TDD가 어떻게 설계에 쓰였는지 감을 잡으실 수 있을 것입니다.
부분중에 한 부분입니다. TDD 개념을 가장 쉽게 접근할 수 있도록 만든
예제인데 무단으로 퍼서 소개할까 합니다. - 사실 본 책 안에 있는 내용은
좀 난해한 편이고 , 이 예제가 더 이해하기 쉽습니다. -
부록 B . 피보나치
이 책의 검토자 중 한 명의 질문에 대한 답으로 나는 피보나치 수열을
테스트 주도로 개발해 올렸다. 몇 명의 검토자들이 이 예를 보고 TDD가
어떻게 작동하는지 이해하는 데 큰 도움이 되었다고 했다. 하지만 이 책에
사용된 예제를 피보나치 예제로 바꾸기에는 피보나치 예제가 너무 짧고,
다양한 TDD 기술을 충분히 보여주지도 못한다. 이 책의 주 예제를 읽은
후에도 여전히 번쩍이는 깨달음을 얻지 못했다면 여기를 잠깐 들여다보고
머릿속 전등이 켜지는지 살펴보자.
첫 번째 테스트는 fib(0) = 0 이라는 걸 보여준다. 구현은 상수를
반환한다.
- 참고로 TDD는 실패하는 Test Code 부터 작성합니다.
- 그리고 그것을 제대로 돌리기 위한 코드를 작성합니다.
public void testFibonacci () {
assertEquals(0 , fib(0));
}
int fib(int n) {
return 0;
}
(함수 하나밖에 안되기 때문에 나는 TestCase 클래스에 직접 해당 코드를
박아 넣구 있다)
두 번째 테스트는 fib(1) = 1 이라는 걸 보여준다.
pulic void testFibonacci () {
assertEquals (0 , fib(0));
assertEquals (1, fib(1));
}
testFibonacciOfOneIsOne 이라는 테스트 메서드를 따로 작성하는 것에 큰
커뮤니케이션 가치가 있어 보이지 않아서 , 두 번째 단언을 같은 메서드
내에 집어 넣어 버렸다.
이게 돌아가도록 하는 덴 몇 가지 방법이 있다. 나는 0을 특별한 경우로
다루는 방법을 쓰겠다.
int fib (int n) {
if (n == 0) return 0;
return 1;
}
테스트 케이스에 있는 중복이 점점 성가시게 느껴지기 시작하는데, 새
캐이스를 추가하면 더 악화되기만 할 것이다. 입력과 예상값으로 구성된
테이블을 통해 테스트가 돌아가게 하면 단언의 공통 구조를 추출할 수
있겠다.
public void testFibonacci () {
int cases[][] = {{0,0} , {1,1}};
for (int i= 0; i < cases.length ; i++)
assertEquals(cases[i][1] , fib (cases[i][0]));
}
이제 다음 케이스를 추가하려면 키보드를 여섯 번만 치면 되고, 줄을 새로
추가할 필요는 없다.
public void testFibonacci () {
int case[][] = {{0,0} , {1,1} , {2,1}};
for (int i=0 ; i < cases.length ; i++)
assertEquals(cases[i][1] , fib(cases[i][0]));
}
당황스럽게도 테스트가 제대로 돌아간다. 우리가 고른 상수 1이 이
케이스에도 들어맞는 값이기 때문에 그런 일이 벌어졌다. 다음 테스트로
넘어가면
public void testFibonacci () {
int case[][] = {{0,0} , {1,1} , {2,1}, {3,2}};
for (int i=0 ; i < cases.length ; i++)
assertEquals(cases[i][1] , fib(cases[i][0]));
}
야호, 이제 실패한다. 이전의 전략(더 작은 입력값을 특별한 경우로 다루는
것)을 똑같이 적용해서 다음과 같이 작성한다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 2;
}
이제 일반화할 준비가 되었다. 우리가 2라고 쓰긴 했지만 정말 2를
뜻한것은 아니고 , 1+1 을 의미한다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 1+1;
}
첫 번째 1은 fin(n-1)로 볼 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + 1;
}
두 번째 1은 fib(n-2)로 볼 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + fib(n-2);
}
이제 좀 정리를 하면, 동일한 구조가 fib(2) 에서도 작동하기 때문에 결국
두 번째 조건을 강화할 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
이렇게 해서 우리는 완전히 테스트로부터 유도된 피보나치를 완성하게
되었다.
이 책의 검토자 중 한 명의 질문에 대한 답으로 나는 피보나치 수열을
테스트 주도로 개발해 올렸다. 몇 명의 검토자들이 이 예를 보고 TDD가
어떻게 작동하는지 이해하는 데 큰 도움이 되었다고 했다. 하지만 이 책에
사용된 예제를 피보나치 예제로 바꾸기에는 피보나치 예제가 너무 짧고,
다양한 TDD 기술을 충분히 보여주지도 못한다. 이 책의 주 예제를 읽은
후에도 여전히 번쩍이는 깨달음을 얻지 못했다면 여기를 잠깐 들여다보고
머릿속 전등이 켜지는지 살펴보자.
첫 번째 테스트는 fib(0) = 0 이라는 걸 보여준다. 구현은 상수를
반환한다.
- 참고로 TDD는 실패하는 Test Code 부터 작성합니다.
- 그리고 그것을 제대로 돌리기 위한 코드를 작성합니다.
public void testFibonacci () {
assertEquals(0 , fib(0));
}
int fib(int n) {
return 0;
}
(함수 하나밖에 안되기 때문에 나는 TestCase 클래스에 직접 해당 코드를
박아 넣구 있다)
두 번째 테스트는 fib(1) = 1 이라는 걸 보여준다.
pulic void testFibonacci () {
assertEquals (0 , fib(0));
assertEquals (1, fib(1));
}
testFibonacciOfOneIsOne 이라는 테스트 메서드를 따로 작성하는 것에 큰
커뮤니케이션 가치가 있어 보이지 않아서 , 두 번째 단언을 같은 메서드
내에 집어 넣어 버렸다.
이게 돌아가도록 하는 덴 몇 가지 방법이 있다. 나는 0을 특별한 경우로
다루는 방법을 쓰겠다.
int fib (int n) {
if (n == 0) return 0;
return 1;
}
테스트 케이스에 있는 중복이 점점 성가시게 느껴지기 시작하는데, 새
캐이스를 추가하면 더 악화되기만 할 것이다. 입력과 예상값으로 구성된
테이블을 통해 테스트가 돌아가게 하면 단언의 공통 구조를 추출할 수
있겠다.
public void testFibonacci () {
int cases[][] = {{0,0} , {1,1}};
for (int i= 0; i < cases.length ; i++)
assertEquals(cases[i][1] , fib (cases[i][0]));
}
이제 다음 케이스를 추가하려면 키보드를 여섯 번만 치면 되고, 줄을 새로
추가할 필요는 없다.
public void testFibonacci () {
int case[][] = {{0,0} , {1,1} , {2,1}};
for (int i=0 ; i < cases.length ; i++)
assertEquals(cases[i][1] , fib(cases[i][0]));
}
당황스럽게도 테스트가 제대로 돌아간다. 우리가 고른 상수 1이 이
케이스에도 들어맞는 값이기 때문에 그런 일이 벌어졌다. 다음 테스트로
넘어가면
public void testFibonacci () {
int case[][] = {{0,0} , {1,1} , {2,1}, {3,2}};
for (int i=0 ; i < cases.length ; i++)
assertEquals(cases[i][1] , fib(cases[i][0]));
}
야호, 이제 실패한다. 이전의 전략(더 작은 입력값을 특별한 경우로 다루는
것)을 똑같이 적용해서 다음과 같이 작성한다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 2;
}
이제 일반화할 준비가 되었다. 우리가 2라고 쓰긴 했지만 정말 2를
뜻한것은 아니고 , 1+1 을 의미한다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return 1+1;
}
첫 번째 1은 fin(n-1)로 볼 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + 1;
}
두 번째 1은 fib(n-2)로 볼 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n <= 2) return 1;
return fib(n-1) + fib(n-2);
}
이제 좀 정리를 하면, 동일한 구조가 fib(2) 에서도 작동하기 때문에 결국
두 번째 조건을 강화할 수 있다.
int fib(int n) {
if (n == 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2);
}
이렇게 해서 우리는 완전히 테스트로부터 유도된 피보나치를 완성하게
되었다.
제가 어디서 본 이야기인데 , TDD 를 단지 단위테스트 용 기법으로
생각하시는 분들이 많다고 합니다. 그러나 TDD는 '분석기법이고,
설계기법' 입니다. 그런것을 염두에 두시고 다시 한번 피보나치 구현을
보시면 TDD가 어떻게 설계에 쓰였는지 감을 잡으실 수 있을 것입니다.