1. getting stuck.
다음 테스트를 보자.
public class WrapperTest {
@Test public void
should_wrap() {
assertThat(wrap("word word",4),is("word\nword"));
}
private String wrap(String s, int length) {
return null;
}
}
wrap 은 length를 받아 받은 문자열을 length만큼 자르고 나머지는 다음줄에 표현한다.
이 경우에는 replaceAll을 이용하여 문제를 해결 할 수 있다.
private String wrap(String s, int length) {
return s.replaceAll(" ","\n");
}
다른 failing test를 추가하자
assertThat(wrap("a dog",5),is("a dog"));
이 경우는 다음과 같이 바꾸어야 한다.
private String wrap(String s, int width) {
return s.length() > width ? s.replaceAll(" ","\n") : s;
}
또 다른 failing test 를 추가하자.
assertThat(wrap("a dog with a bone", 6), is("a dog\nwidth a\nbone"));
다음 코드를 수정하려니 어떻게 해야할지 막히게 된다.
이런 경우를 stuck 상태에 빠졌다고 한다.
이 문제를 해결하기 위해서는 다시 처음부터 해야 한다고 한다.
발생 원인은
처음부터 generate한 예시와 코드를 작성했기 때문이다.
이를 방지하기 위해서 다음과 같이 코드 하라고 한다.
Getting Unstuck
- most degenerate test case를 먼저 작성하고
- 이 복잡함 퀴즈를 아주 작은 스텝씩 올라가자.
- 각 경우에서 해당 테스트를 통과시키도록 specific한 fix를 하는 것이 아니라
- production code를 generalizing하여 테스트가 통과되도록 하자.
2. 다시 처음부터 예시를 살펴 보자!
1. 가장 쉽고 일반적인 것부터 (null 과 공백)
public class WrapperTest {
@Test public void
should_wrap() {
assertThat(wrap(null,1),is(""));
assertThat(wrap("",1),is(""));
}
private String wrap(String s, int width) {
return "";
}
}
2. 간단한 케이스 추가(공백이 필요 없는 문자들)
public class WrapperTest {
@Test public void
should_wrap() {
assertThat(wrap("",1),is(""));
assertThat(wrap("",1),is(""));
assertThat(wrap("x",1),is("x"));
}
private String wrap(String s, int width) {
if(s==null)
return "";
return s;
}
}
<리팩토링한 케이스>
@Test public void
should_wrap() {
assertWraps(null, 1, "");
assertWraps("", 1, "");
assertWraps("x", 1, "x");
}
private void assertWraps(String s, int width, String expected) {
assertThat(wrap(s, width), is(expected));
}
3. 공백이 필요한 케이스
@Test public void
should_wrap() {
assertWraps(null, 1, "");
assertWraps("", 1, "");
assertWraps("x", 1, "x");
assertWraps("xx", 1, "x\nx");
}
private void assertWraps(String s, int width, String expected) {
assertThat(wrap(s, width), is(expected));
}
private String wrap(String s, int width) {
if(s==null)
return "";
if(s.length() <= width)
return s;
else
return s.substring(0,width) + "\n" + s.substring(width);
}
substring으로 width만큼 짜른 후에 \n를 한다.
4. 공백이 여러개인 케이스
public class WrapperTest {
@Test public void
should_wrap() {
assertWraps("xxx", 1, "x\nx\nx");
}
private String wrap(String s, int width) {
if(s.length() <= width)
return s;
else
return s.substring(0,width) + "\n" + wrap(s.substring(width),width);
}
재귀 함수를 이용하여 width가 s.elngth() <= width에 걸릴 때 까지 반복시킬 수 있다.
5. 특이한 변칙적인 케이스
다음 케이스일 경우에는 x\nx를 기대하지만 공백도 하나의 문자로 취급하고 x\n\nx\n와 같은 결과가 나온다.
public class WrapperTest {
@Test public void
should_wrap() {
assertWraps("x x", 1, "x\nx");
}
private String wrap(String s, int width) {
if(s==null)
return "";
if(s.length() <= width)
return s;
else
return s.substring(0,width) + "\n" + wrap(s.substring(width).trim(),width);
}
}
trim을 통해 이를 처리 한다.
6. 더 특이한 케이스
public class WrapperTest {
@Test public void
should_wrap() {
assertWraps("x xx", 3, "x\nxx");
}
private String wrap(String s, int width) {
if(s.length() <= width)
return s;
else {
int breakPoint = s.lastIndexOf(" ",width);
if(breakPoint == -1)
breakPoint = width;
return s.substring(0, breakPoint) + "\n" + wrap(s.substring(breakPoint).trim(), width);
}
}
}
만약 width 이전에 space(공백)이 나온 경우에는 width의 상관없이 바로 공백 한 후 width를 다시 세서 공백을 만들어 줘야 한다.
이 경우에 lastIndexOf를 이용하여 공백을 넣을 부분을 찾는다.
lastIndexOf의 첫번째 파라미터는 찾을 문자열 두번째는 어디서 부터 시작할지이다.
x xx경우
x\nx
1 2 3 인 경우이고 3번 x부터 뒤에서 몇번째인지 찾는다. 이 경우 breakPoint는 결국 1이 된다.
만약 찾을 수 없다면 -1를 반환한다.
7. 원하는 테스트
assertWraps("four score and seven years ago our fathers brought forth upon this continent", 7, "four\nscore\nand\nseven\nyears\nago our\nfathers\nbrought\nforth\nupon\nthis\ncontine\nnt");
제대로 작성 됬다면 정상적으로 동작하게 될 것이다.
'TDD, CleanCode' 카테고리의 다른 글
Next.js 와 Redux 테스트(Jest, Enzyme)를 해보자. (0) | 2019.08.11 |
---|---|
React 테스트 (Jest, Enzyme) (0) | 2019.08.10 |
클린코더스 강의 리뷰 5. TDD 예제 (Prime factors) (0) | 2019.05.21 |
클린코더스 강의 리뷰 5. TDD 예제 (Bowling Game) (0) | 2019.05.20 |
클린코더스 강의 리뷰 5. TDD의 기본 예시 (0) | 2019.05.13 |