Unit Tests할 때 given-when-then pattern 사용하시나요?

2013-02-05 15:21

기민하게 해당문제에 대해서만 작업을 해야된다고 책에 되어있지만 자꾸 TDD할 때 given-when-then pattern을 사용하게 되네요. 처음에는 그냥 작성하다 중복 제거 단계에서 given-when-then으로 넘어갈 때도 있지만 처음부터 pattern기반으로 테스트 코드를 먼저 작성하다보니 빨간 막대 패턴을 빨리 적용하지 못하는 문제가 있는 것 같습니다.

어떤 문서에 보니 acceptance tests 할 때만 사용한다는 문서도 있고요. 어떻게 작업하시고 계신가요? 관련 서적있으면 추천 부탁드립니다.

12개의 의견 from SLiPP

2013-02-05 18:35

나도 단위 테스트를 TDD로 구현할 때 given-when-then 패턴 사용하려고 노력해 봤는데 생각보다 필요성을 잘 느끼지 못해서 중도에 포기하는 경더라. 혹시 단위 테스트에서도 given-when-then 패턴 사용하면 유용함을 느끼는 경우가 많이 있나?

간혹 mockito와 같은 mock 프레임워크를 사용하는 경우에는 유용할 수 있겠다라는 생각이 드는데 그 경우에도 이미 mockito의 when, verify 메소드를 통해서 일정 부분 구분할 수 있지 않을까?

acceptance test를 만들 때는 given-when-then 패턴이 유용한 경우 많을 듯 한데 단위 테스트까지는 아직 그 실효성을 잘 느끼지 못하고 있다.

2013-02-05 19:12

안녕하세요?

저는 "진지하게 TDD를 하고자 한다면 Given/When/Then은 반드시 써야 하는 템플릿"이라고 생각합니다.

흔히 개발자들이 TDD를 책으로 배워서 사용하게되면 익숙해질때까지는 곧잘 인지부조화(cognitive dissonance)상태에 빠지게 됩니다.

막상 만들면 좋긴 한데 이런저런 이유로 안하게 된다던가, 그닥 도움이 안된다는 식으로 생각이 변하게 되죠. 이점을 느껴도 지속되지 못하거나 생각이 바뀌게 되는데는 사실 여러가지 이유가 있지만 그 중 하나가 목적의식 결여와 테스트 케이스 문맥 상실에 있습니다. 별것 아닌것처럼 들릴 수 있지만, 이건 성공적인 TDD를 이어나가기 위해서는 매우 중요한 요소입니다.

Given/When/Then은 이 부분에서 단순하지만 막강한 위력을 발휘합니다.

또한 이런 스타일은 개발때 뿐 아니라 유지보수 할 때도 큰 도움이 됩니다.

acceptance test(이하 AC test)때 유용하다는 이야기를 하셨는데, 전 반대로 AC test나 BDD 할 때보다 TDD/Unit Test 작성시에 더 유용한 스타일이라고 생각합니다. 왜냐하면, 아쉬운 이야기이긴 합니다만 AC test나 BDD는 작성하게 될 상황이 매우 드물고 TDD시에 Given/When/Then이 습관이 된다면 BDD때는 당연하게 사용됩니다.

참고로 NHN의 신입사원들은 TDD를 배울때 반드시 써야 하는 스타일로 Given/When/Then을 가르치고 있습니다.

:)

그리고 여유되시면 다음 링크글도 한 번 읽어보세요 http://blog.doortts.com/169

2013-02-05 20:41

@@doortts TDD에서 Given/When/Then 패턴을 사용한 예제 소스나 관련 문서를 볼 수 있을까? 지금까지 이 패턴을 쓰려고 노력해 보았으나 어떻게 하는 것이 맞을지에 대해서 잘 모르겠더라고. 추천해 주고 싶은 자료 있다면 공유 좀 해주라. 나도 학생들에게 TDD 전파해야 되는데 mockito 개발자가 이야기하는 것처럼 반드시 해야할 부분이라면 나도 함 연습해 봐야겠다. 하지만 mockito의 경우에는 Given/When/Then이 명확하게 보이는 경우가 많은데 그 외에는 잘 모르겠더라. 나도 계속 연습해 봐야겠네.

2013-02-06 10:03

좀더 생각해 봐야겠네요. Given/When/Then 패턴 적용은 테스트코드의 읽기 쉬워져서 계속 사용네요. 그런데 TDD하다 보면 자꾸 Given/When/Then 기반코드를 작성하고 있느 저를 발견하면 뭐하나 생각도 들고요.

책보다가 인터넷하고 있는 자신을 발견할 때의 기분. 두 분 모두 감사합니다.

2013-02-06 10:36
- (void)testThemeDirectoryLayoutInvalid {
    [self givenUnitTestBundleTheme:@"themeDirectoryFailure"];
    [self givenCopyResourceFileIsExistsOnMainBundle:@"xxxxxxxx.xml"];
    [self thenPathComponentsLoadingStatus:ThemeLoadingStatus.errorImageDirectoryNotFound];
}


- (void)testDefaultThemeSaveToThemesOnNSUserDefaults {
    [self givenDefaultTheme];
    [self whenNaverAppLaunchedAtBundleVersionUpdate];
    [self thenDefaultThemeHasBeenSavedToNSUserDefaults];
}


- (void)testLoadingVisitHistoryGroups {
    [self givenVisitList:todayVisit(@"url", @"title"), todayVisit(@"url1", @"title1"), nil];
    [self thenTodayGroups:history(@"url", @"title"), history(@"url1", @"title1"), nil];
    
    [self givenVisitList:yesterdayVisit(@"url", @"title"), nil];
    [self thenYesterdayGroups:history(@"url", @"title"), nil];


    [self givenVisitList:aWeekVisit(@"url", @"title"), nil];
    [self thenAWeekGroups:history(@"url", @"title"), nil];
    
    [self givenVisitList:twoWeeksVisit(@"url", @"title"), nil];
    [self thenTwoWeeksGroups:history(@"url", @"title"), nil];
}

제가 요즘 짜고 있는 테스트 코드요~~~mockito Given/When/Then 표현 방법 아니구요~~~

2013-02-06 10:46

@강용석 에디터에 보면 코드를 넣을 수 있는 위키 태그 있다. 그 안에 넣어주면 소스 코드 확인하기 더 좋겠다. 상단에 보면 <> 표시 보이지. 이 놈 활용해서 넣어봐라.

페북에 올렸더니 의견들이 올라왔네. 다음 문서들 참고해 보면 좋겠다.

정상혁 군의 답변 * http://dev.naver.com/scm/viewvc.php/trunk/naver-java-client-samples/src/test/java/com/naver/openapi/shorturl/ShortUrlApiClientJaxbImplTest.java?revision=8&root=naverapis&view=markup * http://blog.benelog.net/2688165

given 절에 넣을지 when 절에 넣을지 고민이 될때가 많은데 then에 들어갈 내용과 대칭성이 있는지의 여부로 많이 판단해요.. 장점은 테스트의 목적과 관심사를 의식하게 되고, 절마다의 의도가 있으니 테스트코드 리팩토링을 할 때도 길잡이가 되어줘요.. given이나 then이 있는 내용이 반복되면 extract method혹은 fixture생성용 클래스로 분리를 하죠..

2013-02-06 12:02

@자바지기 재성형 패북봐서 코드를 올렸는데요. 제가 명확하게 작성하지 않아서인지, 페북 답변이 mockito Given/When/Then 표현 방법하고 혼돈하신 것 같아서 올렸어요.

clean code에서 소개된 given/when/then 패턴을 적용하다보니 자꾸 Test case를 위 코드처럼 짜게되더라구요. objective-c 환경에서 적용하다보니 실천하기가 많이 까다로웠어요. refactoring 기능이 거의 없고 신뢰성이 부족했던 Xcode에서 Given/When/Then을 적용하기가 시간적으로 무리가 있었구요. 근데 한번 익숙해지니 계속 위 코드처럼 짜네요. ㅋㅋ

TDD책 하나 써주세요~~~~

2013-03-06 14:47

저는 GWT(given-when-then) 자주 쓰지는 않습니다. 꼼꼼하게 테스트를 작성하기 보다는 적당히 테스트를 작성하고 버그가 나오면 어떻게 하면 이러한 문제를 쉽게 재현하는 테스트를 작성할 수 있는지 고민하는게 더 재밌더라구요. 이 과정에서 디자인 개선이 발생할 수 있고 진단하기 쉬운 모델을 고안할 수 있는 것 같고요. GWT 보다는 테스트 코드의 중복관리가 훨씬 중요하게 느껴지고 불필요한 중복 테스트를 줄여나가는 것이 더 의미가 있는 것 같고요.

저는 적당히 느슨한 TDD가 좋던데요. 켄트백은 이렇게 말했죠. 정확히 기억나지는 않지만 대충 이런 의미였어요. "처음 만들 땐 약 30개 정도의 테스트를 작성했었는데 지금 다시 작성한다면 6개로 끝낼 것 같다. 두려우면 더 작성해야 한다."

GWT는 UT보다는 AT에 좀더 유익하게 느껴지긴해요.

2013-03-06 18:13

재작년에 진행했던 SI 프로젝트에서 저희 팀이 Given-When-Then 템플릿을 사용해 작성했던 테스트 케이스는 이런 식이었습니다. 이렇게 작성하면 log4j로 테스트 케이스와 결과가 출력되기 때문에 결과를 문서로 만들면 spec 목록이 됩니다.

    @Test
    public void 회원아이디로_회원정보_획득() {
    	Option<Member> result;
    	
    	given("크레딧을 가지고 있는 '%s'라는 사용자가 가입되어 있다", TEST_MEMBER_ID); {
    	    // Test DB에 이미 등록되어 있음
    	}
    	when("회원ID로 개인 상세정보 조회 시"); {
    	    result = service.findMember(TEST_MEMBER_ID);
    	}
    	then("얻어진 회원 정보의 회원ID가 %s와 동일해야 함", TEST_MEMBER_ID); {
    	    assertThat("데이터를 가지고 오지 못함", result.isEmpty(), is(true));
    	    assertThat(result.get().getMemberId(), is(TEST_MEMBER_ID));
    	}
    }
2015-06-18 21:50

Java8 사용하시면 이런 방법도 있습니다.

  @Test
  public void testNullSafeTrim() {
    /* Given */
    final String expected = "result";
    final String input = "  " + expected + "  ";

    test("assertThat",
         "nullSafeTrim(\"  result  \") should return \"result\".")
    .when(() ->
      nullSafeTrim(input)
    )
    .then(actual ->
      assertThat(actual.length()).isEqualTo(expected.length())
    )
    .then(actual -> {
      assertThat(actual).isEqualTo(expected);
    });
  }

'테스토스테론'이라고 제가 필요해서 만들어 쓰고 있는 간단한 테스트 헬퍼 프레임워크입니다. Test0ster1

Given부분을 API안에 추가할 예정인데, 어떻게 넣어볼까 고민중입니다. 몇가지 생각중인 방법

의견 추가하기

연관태그

← 목록으로