TDD 실습 예제(1)

TDD 실습 예제 (1)

msbaek님의 강의를 보고 작성한 글입니다. 링크

개요 & 용어 정의

개요

  • 소인수 분해 알고리즘 작성
  • 입력 값의 소인수 구하기
  • TDD로 개발을 진행합니다.(테스트 -> 해결 -> 리팩토링 순서)

    소수

  • 1과 자기자신외의 정수로 나누어 떨어지지않는 정수

    소인수

  • 어떤 정수를 소수만의 곱으로 나타낼 때 그 인수가 되는 각각의 소수

0. nothing

public void nothing으로 시작

1
2
3
4
5
6
public class PrimeFactorsTest {
@Test
public void nothing() {

}
}
  • 이 테스트를 통해서 현재 프로젝트가 테스트 가능한 환경인지 확인한다.

1. canFactorIntoPrimes for 1

1.1 실패 테스트 추가

createFactorInfoPrimes for 1

  • 가장 단순한 1에 대한 소인수를 구하는 테스트를 진행한다.
  • 1을 제외한 소인수들을 구해야한다. 그래서 1의 소인수는 없다.

1.2 Test 성공시키기

1
2
3
4
...
private List<Integer> of(int n) {
return Arrays.asList();
}

1.3 Refactor

1
2
3
4
5
6
7
8
9
@Test
public void canFactorIntoPrimes() {
assertEquals(list(),of(1));
}

private List<Integer> list() {
return Arrays.asList();
}
...
  • of()의 리턴타입은 Integer인 반면에 테스트에서는 Object이므로 Arrays.asList()를 Extract 해준다.

2. canFactorIntoPrimes for 2

2에 대한 소인수를 찾아야하는데 여기서 Variable arguments(가변인자)라는걸 사용했는데 일단 한번 코드로 보자

2.1 실패테스트 추가 및 테스트 성공시키기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void canFactorIntoPrimes() {
assertEquals(list(),of(1));
// 2에 대한 테스트 추가
assertEquals(list(2),of(2));
}

private List<Integer> list(Integer... n) {
return Arrays.asList(n);
}

private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
if(n == 2)
factors.add(2);
return factors;
}
...
  • list()에서 가변인자 ‘…’ 를 통해 파라미터를 배열로 바꾸어준다.
  • 가변인자 : ‘해당 타입의 객체가 0개부터 여러개까지 매개변수로 올 수 있다.’ 라는 뜻
  • Java 1.5부터 사용가능한 가변인자는 파라미터 맨 끝에만 사용가능하다.(오버로딩 주의)

2.2 Refactor

  • n==2 보다 n>1이 좀 더 Generic하므로 수정한다.
    1
    2
    3
    4
    5
    6
    private List<Integer> of(int n) {
    List<Integer> factors = new ArrayList<Integer>();
    if(n > 1)
    factors.add(2);
    return factors;
    }

3. canFactorIntoPrimes for 3

3.1 실패 테스트 추가

1
2
3
4
5
6
7
8
@Test
...
public void canFactorIntoPrimes() {
assertEquals(list(),of(1));
assertEquals(list(2),of(2));
assertEquals(list(3),of(3));
}
...

3.2 테스트 성공시키기

1
2
3
4
5
6
7
8
...
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
if(n > 1)
factors.add(n);
return factors;
}
...
  • 모든 테스트는 최소한의 코드로 성공시키려고한다.

3.3 Refactor

assertEquals가 반복되는게 보기 안좋다. Extract method시키자!

Extract Variable

1
2
3
4
5
6
7
8
9
@Test
public void canFactorIntoPrimes() {
List<Integer> list = list();
int n = 1;
assertEquals(list, of(n));
assertEquals(list(2), of(2));
assertEquals(list(3), of(3));
}
...

Extract Method

1
2
3
4
5
6
7
8
9
10
11
@Test
public void canFactorIntoPrimes() {
assertPrimeFactors(list(), 1);
assertPrimeFactors(list(2), 2);
assertPrimeFactors(list(3), 3);
}

private void assertPrimeFactors(List<Integer> list, int n) {
assertEquals(list, of(n));
}
...

Change Signature

image

  • Change Signature를 통해서 assertPrimeFactors 메소드의 파라미터 순서를 바꿔준다(바꾸는게 보기편하다고 한다..)
    1
    2
    3
    4
    5
    6
    @Test
    public void canFactorIntoPrimes() {
    assertPrimeFactors(1, list());
    assertPrimeFactors(2, list(2));
    assertPrimeFactors(3, list(3));
    }

4. canFactorIntoPrimes for 4

  • 4의 소인수는 2,2

4.1 실패 테스트 추가

1
2
3
4
5
6
7
@Test
public void canFactorIntoPrimes() {
assertPrimeFactors(1, list());
assertPrimeFactors(2, list(2));
assertPrimeFactors(3, list(3));
assertPrimeFactors(4, list(2,2));
}

4.2 테스트 통과시키기

1
2
3
4
5
6
7
8
9
10
11
12
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
if(n > 1) {
if(n % 2 == 0) {
factors.add(2);
n /= 2;
}
if(n > 1)
factors.add(n);
}
return factors;
}
  • 간단하게 2로 나누었을 때 나머지가 0이면 2ArrayList에 추가

4.3 Refactor

  • factorsn을 더하는 부분을 if(n>1) 밖으로 뺀다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    private List<Integer> of(int n) {
    List<Integer> factors = new ArrayList<Integer>();
    if(n > 1) {
    if(n % 2 == 0) {
    factors.add(2);
    n /= 2;
    }
    }
    if(n > 1)
    factors.add(n);


    return factors;
    }

5. canFactorIntoPrimes for 8

  • 그 전 숫자들은 모두 Pass, 8에서 걸림

5.1 실패 테스트 추가

1
2
3
4
5
6
7
public void canFactorIntoPrimes() {
assertPrimeFactors(1, list());
assertPrimeFactors(2, list(2));
assertPrimeFactors(3, list(3));
assertPrimeFactors(4, list(2,2));
assertPrimeFactors(8, list(2,2,2));
}

5.2 테스트 성공 시키기

1
2
3
4
5
6
7
8
9
10
11
12
13
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
if(n > 1) {
while(n % 2 == 0) {
factors.add(2);
n /= 2;
}
}
if(n > 1)
factors.add(n);

return factors;
}
  • 간단하게 if(n % 2 == 0) 부분을 while문으로 교체해서 해결

6. canFactorIntoPrimes for 9

6.1 실패 테스트 추가

1
2
3
4
5
6
7
8
public void canFactorIntoPrimes() {
assertPrimeFactors(1, list());
assertPrimeFactors(2, list(2));
assertPrimeFactors(3, list(3));
assertPrimeFactors(4, list(2,2));
assertPrimeFactors(8, list(2,2,2));
assertPrimeFactors(9, list(3,3));
}

6.2 테스트 성공 시키기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
if(n > 1) {
while(n % 2 == 0) {
factors.add(2);
n /= 2;
}
while(n % 3 == 0) {
factors.add(3);
n /= 3;
}
}
if(n > 1)
factors.add(n);

return factors;
}
  • 간단하게 3에 대한 while문 추가로 테스트 패스

6.3 Refactor

while문의 중복이 생기게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
int divisor = 2;

if(n > 1) {
while(n % divisor == 0) {
factors.add(divisor);
n /= divisor;
}
while(n % 3 == 0) {
factors.add(3);
n /= 3;
}
}
if(n > 1)
factors.add(n);

return factors;
}

  • 첫번째 while문의 중복되는 2에 대해서 extract variable한다.
1
2
3
4
5
6
7
private List<Integer> of(int n) {
List<Integer> factors = new ArrayList<Integer>();
for(int divisor = 2;n > 1;divisor++)
for(;n % divisor == 0; n /= divisor)
factors.add(divisor);
return factors;
}
  • while문들을 for문으로 변경해주면서 조건들을 for문에 담는다. for문의 조건상 마지막 if(n>1)은 의미가 없기때문에 삭제한다.

0%