전통적으로 잘못 인식되어 있는 테스트 메소드의 리팩터링

55 views
Skip to first unread message

펭귄너구리

unread,
Apr 24, 2010, 11:54:37 PM4/24/10
to xper
TDD시에 만들어지는 중복 코드에 대한 의견입니다.
저는 이렇게 설명하곤 합니다만, 왠지 논란(?)의 소지가 있을 것 같아서 올려봅니다.

TDD 안티패턴(Anti-pattern), 전통적으로 잘못 인식되어 있는 테스트 메소드의 리팩터링
http://blog.doortts.com/123

어떻게 생각하시나요? :)

--
한국 XP 사용자 모임(http://xper.org) 메일링 리스트에 가입하셨기에 이 메시지를 보내드립니다. Google 그룹스 "xper" 그룹
이 그룹에 게시하려면 다음 주소로 이메일을 보내주십시오.
xp...@googlegroups.com
이 그룹에서 탈퇴하시려면 다음으로 이메일을 보내주십시오.
xper+uns...@googlegroups.com
추가 옵션을 보려면 http://groups.google.com/group/xper?hl=ko?hl=ko의 그
룹을 방문하십시오.

Youngrok Pak

unread,
Apr 25, 2010, 12:47:42 AM4/25/10
to xp...@googlegroups.com
리팩토링은 중복을 제거하고 의도를 드러내는 과정이죠. 아마도 펭귄너구리님은 중복을 setUp으로 제거하면 테스트 시나리오의 의도가 숨기 때문에 그것을 문제라고 보시는 것 같습니다. 문제 인식에는 저도 동의하구요.

저는 이런 식으로 해결하곤 합니다.

public void setUp() {
    tenThousandsAccount = new Account(10000);
}

public void testDeposit() {
    assertEquals(11000, tenThousandsAccount.deposit(1000).getBalance());
}

또 만약 account를 만드는 과정이 생성자 한 줄이 아니라 좀더 복잡하다면,

public void testDeposit() {
    account = prepareTenThousandsAccount();
    account.deposit(1000);
    assertEquals(11000, account.getBalance());
}

이렇게 될 수도 있겠지요. 같은 한 줄이긴 하지만 여기는 10000이라는 숫자의 중복이 제거되어 있죠.

어쨋든, 저는 And의 영신을 받아들여서(?) 중복도 제거하고 의도도 드러내는 방법이 항상 있다고 믿쑵니다~

2010/4/25 펭귄너구리 <doo...@gmail.com>

아샬

unread,
Apr 25, 2010, 9:00:38 AM4/25/10
to xp...@googlegroups.com
제 경우엔 RSpec을 이용해서 작업하는데 익숙해서인지

테스트 케이스 자체를 작게 하는 게 더 나아보입니다.

해법 자체는 영록님과 유사한데,

TestCase: "10,000원이 들어있는 계좌"
- setUp: Account 생성
- 입금 테스트
- 출금 테스트

같은 식으로 처리하는 거죠.

이쪽이 Mock Object 등을 사용할 때도 훨씬 깔끔한 것 같습니다.

RSpec의 경우엔 describe 안에 describe을 또 써줘서 특정 Account 케이스를 처리하는데

JUnit에선 어떤 식으로 되는지 잘 모르겠습니다.

계층적으로 가능하다면 그렇게 하면 되고,

불가능하다면 별개의 클래스를 잡아주면 될 듯 하네요. (AccountWithXxxTest 같은?)

2010년 4월 25일 오후 1:47, Youngrok Pak <pak.yo...@gmail.com>님의 말:

June Kim

unread,
Apr 25, 2010, 9:40:45 AM4/25/10
to xp...@googlegroups.com
좋은 질문입니다.

일단 저는 중복을 되도록 줄이는 쪽을 택합니다. 왜냐하면 테스트 코드 간의 중복에서 데어본 적(테스트 코드 수정이 필요할 때
여기 저기 흩뿌려진 중복 찾아가며 고쳐야 함 -- 이게 귀찮아서 기능 추가를 안하기도 함)이 있기 때문입니다. 그렇게 되면
테스트 코드가 코드 변화를 보완해주는 것이 아니라 오히려 짐이 되지요.

그리고 중복을 일단 줄인 상태에서 가독성을 높이는 방법을 찾습니다.

public class BaseAccountWithDeltasTest {
private Account account;

@Before
public void setUp(){
base = new Account(10000);
delta = 1000;
}

@Test
public void testDeposit() throws Exception {
account.deposit(delta);
assertEquals(base+delta, account.getBalance());
}

@Test
public void testWithdraw() throws Exception {
account.withdraw(delta);
assertEquals(base-delta, account.getBalance());
}
}


2010/4/25 펭귄너구리 <doo...@gmail.com>:

June Kim

unread,
Apr 25, 2010, 9:44:51 AM4/25/10
to xp...@googlegroups.com
중복 제거와 가독성 간의 선택에 대해 예전에 썼던 댓글을 인용합니다:

http://www.ologist.co.kr/507

cavin님의 코드는 일종의 극단적 가독성을 추구하신 거네요. 이 문제에만 국한해 본다면 저는 그냥 조건문을 그대로
두겠습니다. 코드는 항상 독자를 일차 고려대상으로 해야하는데, 그 정도는 독자가 쉽게 이해할 것 같습니다. 또, 때로는 말로
길게 풀어쓰는 것이 가독성을 떨어뜨리기도 합니다. 뭔가 장황해 보이니까요. 우리는 산문보다 운문(가령 하이쿠)에서 배울 것이
많습니다.

만약 코드가 비지니스 로직이 들어가고 그 로직이 domain-rich하다면 되도록 가독성을 추구하겠지만(가독성은 독자에 따른
상대적 개념이라는 것을 명심하면서), 저는 때로 가독성을 손해보면서까지 중복을 줄이기도 합니다.

객체지향에서 그걸 하다 보면 흥미로운 객체들을 발견합니다. 함수형에서 그렇게 하다 보면 흥미로운 함수와 함수의
함수(functional)를 발견합니다. 이 "흥미로운 무엇"은 강력합니다. 내가 전에 모르던 것을 배우게 됩니다. 그리고
종종 이것은 프로그래머의 울타리를 넘어서 영향력을 끼치기도 합니다. 고객들의 대화가 바뀔 수도 있습니다.

객체지향을 하면서 흥미로운 객체들을 발견하지 못한다면 너무 고리타분한 코딩은 아닐까 생각합니다.

2010/4/25 June Kim <june...@gmail.com>:

June Kim

unread,
Apr 25, 2010, 9:47:48 AM4/25/10
to xp...@googlegroups.com
2010/4/25 June Kim <june...@gmail.com>:

> 좋은 질문입니다.
>
> 일단 저는 중복을 되도록 줄이는 쪽을 택합니다. 왜냐하면 테스트 코드 간의 중복에서 데어본 적(테스트 코드 수정이 필요할 때
> 여기 저기 흩뿌려진 중복 찾아가며 고쳐야 함 -- 이게 귀찮아서 기능 추가를 안하기도 함)이 있기 때문입니다. 그렇게 되면
> 테스트 코드가 코드 변화를 보완해주는 것이 아니라 오히려 짐이 되지요.
>
> 그리고 중복을 일단 줄인 상태에서 가독성을 높이는 방법을 찾습니다.
>
> public class BaseAccountWithDeltasTest {
> private Account account;
>

예제 쓸 때에도 TDD로 올려야겠네요. 실수가 있어 정정합니다.

private int base, delta;

> @Before
> public void setUp(){


base = 10000;
delta = 1000;
account = new Account(base);

펭귄너구리

unread,
Apr 26, 2010, 4:10:14 AM4/26/10
to xper
제가 가진 의문과 논란 점에 답이 되었습니다.

적절한 작명과 함께 사용하면 영록님의 답글과 창준님의 답글이
베스트 선택이 될 것 같습니다.

출처를 밝히고
아래와 같은 식으로 책에 인용해도 될런지요? :)

http://blog.doortts.com/123

ps. 왠지 제가 악당(villain)같다는 느낌이 문득...


On 4월25일, 오후10시47분, June Kim <junea...@gmail.com> wrote:
> 2010/4/25 June Kim <junea...@gmail.com>:


>
> > 좋은 질문입니다.
>
> > 일단 저는 중복을 되도록 줄이는 쪽을 택합니다. 왜냐하면 테스트 코드 간의 중복에서 데어본 적(테스트 코드 수정이 필요할 때
> > 여기 저기 흩뿌려진 중복 찾아가며 고쳐야 함 -- 이게 귀찮아서 기능 추가를 안하기도 함)이 있기 때문입니다. 그렇게 되면
> > 테스트 코드가 코드 변화를 보완해주는 것이 아니라 오히려 짐이 되지요.
>
> > 그리고 중복을 일단 줄인 상태에서 가독성을 높이는 방법을 찾습니다.
>
> > public class BaseAccountWithDeltasTest {
> > private Account account;
>
> 예제 쓸 때에도 TDD로 올려야겠네요. 실수가 있어 정정합니다.
>
> private int base, delta;
>
> > @Before
> > public void setUp(){
>
> base = 10000;
> delta = 1000;
> account = new Account(base);
>
>
>
> > base = new Account(10000);
> > delta = 1000;
> > }
>
> > @Test
> > public void testDeposit() throws Exception {
> > account.deposit(delta);
> > assertEquals(base+delta, account.getBalance());
> > }
>
> > @Test
> > public void testWithdraw() throws Exception {
> > account.withdraw(delta);
> > assertEquals(base-delta, account.getBalance());
> > }
> > }
>

> > 2010/4/25 펭귄너구리 <door...@gmail.com>:

Youngrok Pak

unread,
Apr 26, 2010, 5:42:43 AM4/26/10
to xp...@googlegroups.com
이 케이스에서는 창준님 코드가 좀더 좋은 해법인 것 같습니다. 코드로는 창준님 해법을 제시하고, 그냥 'extract method나 적절한 naming 등의 간단한 방법으로 해결할 수 있는 경우도 있다'는 정도의 코멘트를 붙이면 어떨까 싶네요.

그리고, 자바 코드라서 아샬님의 이야기가 감이 안 올 수도 있는데, 이걸 테스트 컨텍스트를 작게 만든다는 관점으로 볼 수도 있을 것 같습니다. 테스트 클래스 하나에 테스트 메서드가 2~3개 뿐이라면 그냥 setUp에 넣어놓고 별 거 안해줘도 테스트 시나리오를 읽는데 무리가 없겠죠. 클래스 이름에 테스트 시나리오의 맥락을 담으면 되구요.

어쨋든 요는 중복 제거와 의도 드러내기는 양립할 수 있고, 그 방법은 다양한 형태로 나타날 수 있다는 게 아닐까요.


2010/4/26 펭귄너구리 <doo...@gmail.com>

June Kim

unread,
Apr 26, 2010, 6:15:15 AM4/26/10
to xp...@googlegroups.com
2010/4/26 펭귄너구리 <doo...@gmail.com>

제가 가진 의문과 논란 점에 답이 되었습니다.

적절한 작명과 함께 사용하면 영록님의 답글과 창준님의 답글이
베스트 선택이 될 것 같습니다.

출처를 밝히고
아래와 같은 식으로 책에 인용해도 될런지요? :)

네. 그렇게 하세요. 이런 "이쪽이냐 저쪽이냐"(그리고 나는 이런 이유로 저쪽을 고르고 이쪽을 고르지 않겠다 등의 이유도 있다면)하는 케이스들이 많으면 상당히 도움이 되는 책이 될 것 같습니다. 우리가 했던 패널토론과 비슷하죠.


Reply all
Reply to author
Forward
0 new messages