TDD 진행 시 생긴 두 가지 의문점

356 views
Skip to first unread message

ChungHee Lee

unread,
Dec 6, 2009, 11:29:18 PM12/6/09
to xp...@googlegroups.com
안녕하세요, 이충희입니다.
점심 맛있게 드셨나요? ^^ 
 
현재 진행하고 있는 프로젝트에서 TDD를 사용하고 있는데요,
TDD를 진행하면서 생긴 두 가지 현상에 대하여 공유하고, 의견을 나누고 싶어서 메일을 쓰게 되었습니다.
 
1. 클래스 멤버 변수를 메소드 내부에서 사용하지 않고, 파라미터를 통해 호출하도록 코딩하게 되는 것...
 
예를 들어서 설명을 해 보면...
 
종전에는 클래스 멤버 변수를 다음과 같이 메소드에서 사용하였습니다.
 
class preTDD {
   
   private int isThisUsed;   // 메소드에서 사용하게 되는 멤버 변수
 
   public void methodToTest() {

       if( isThisUsed ) // 멤버 변수를 사용함
         processA();
 
       ....
   }
 
   public void methodToTestCaller() {  // methodToTest 메소드를 호출함
      methodToTest();
   }
}
 
이와 같이 하면, isThisUsed 멤버 변수에 따라 methodToTest() 메소드의 행동이 바뀌기 때문에
methodToTest() 메소드를 테스트하기가 어려워집니다.
 
따라서 다음과 같이 코딩을 하게 되더군요
 
class postTDD {
   
   private int isThisUsed;   // 이 멤버 변수는 여전히 필요합니다...그렇지만...
 
   public void methodToTest(int isThisUsed) {

       if( isThisUsed ) // 얘는 멤버 변수가 아니라, 파라미터입니다.
         processA();
 
       ....
   }
 
    public void methodToTestCaller() {  // 호출 방법이 바뀜
      methodToTest(this.isThisUsed);
   }
}
 
메소드의 동작은 같아지지만,
호출할 때 파라미터를 넣어준다는 점이 다릅니다.
 
제가 생각하는 장/단점은
 
<장점>
- 메소드 간의 의존성이 적어진다.
- 유닛테스트를 쉽게 할 수 있다.
 
<단점>
- 불필요한 파라미터가 늘어난다.
 
정도입니다.
TDD를 적용해 보셨던 다른 분들은 어떠셨나요?
 
2. public 메소드의 남발(?)
 
외부에서 전혀 사용되지 않는 메소드는 보통 private으로 선언하게 되는데요...
 
public 메소드만을 사용하여 유닛테스트를 만들려고 하다 보니
유닛테스트 하나를 성공시키기 위하여 작업하는 양이 많아지더군요
 
해서
다른 클래스에서는 전혀 사용되지 않는 메소드이지만, 오직 유닛테스트에 사용하기 위하여 public으로 메소드를 선언한 경우가 꽤 많았습니다.
이런식으로 하다보니, 클래스의 대부분이 public 메소드로 구성된 경우도 많았구요
 
이렇게 하면 유닛테스트 하기에는 편하지만, 객체지향의 원칙을 깨버리는 것이 아닐까 하는 생각이 듭니다.
 
다른 분들은 어떤 식으로 테스트 케이스를 작성하고 계시는지요?
 
 
 
 

변신철

unread,
Dec 6, 2009, 11:57:31 PM12/6/09
to xper
1. 함수의 동작이 달라진다면 제법 중요한 멤버인가 보군요??
저는 그런 경우 if문 보다 state pattern을 이용해서 if문 자체를 없애 버리고
테스트 해야 하는 메소드를 좀더 간결하게 수정합니다.
글쿠 대개의 경우 파라미터가 불필요하다는 느낌이 든다면
리팩토링의 여지가 있을 거라 생각이 듭니다... 구조적으로 다른 전략으로 수정을 하는거지요..

2. 케익썰기 모델로 바꾸어 볼 필요도 있습니다.
private 메소드 하나하나를 단위 테스트 하는 것도 유용하지만
그것을 사용하는 public 메소드만을 위한 단위 테스트를 만들 수 없는지 고민해 보면 좋겠지요...
김밥해체하기 보다 김밥썰기를 하도록 애를 쓰면 private을 단위 테스트하는 부분이 줄어 들기도 하거든요...

June Kim

unread,
Dec 7, 2009, 12:06:26 AM12/7/09
to xp...@googlegroups.com
1, 2번이 결국 비슷한 이야기네요. FAQ에 등장할 단골메뉴입니다.


1번. TDD를 하다보면 코드가 전반적으로 functional로 가는 경향이 있습니다. 저는 대부분 변수는 파라미터로 넘기는
방식으로 탄생시킨 다음, 여러 메소드에서 그 변수를 필요로 하면(즉, 파라미터 리스트에서 메소드간 중복이 발생하면), 그 때
리팩토링을 합니다. 몇가지 방식이 있는데 그 중 하나가 인스턴스 변수로 바꾸는 것이죠.

실제 코드 예(단순화된 예가 아니고)를 보여주시면 제가 비슷한 경우에 어떻게 했는지 말씀드릴 수 있겠습니다.

2번. http://xper.org/wiki/xp/TestingPrivateInterfaces 를 참고하세요. 2004년도에
한창 진행했던 논의입니다.

2009/12/7 ChungHee Lee <dre...@gmail.com>:

Jake Kim

unread,
Dec 7, 2009, 1:18:46 AM12/7/09
to xper
1번에 대해서 제 생각을 말씀드리자면 이충희님께서 단점으로 생각하시는 불필요한 파라메터의 증가는 단점이 아닌 경우가 많은 것 같
습니다. 예로 드신 코드를 보면 isThisUsed 라는 변수를 파라메터로 넘기는 것은 그 변수의 값을 어느 곳에서 결정을 하느
냐에 대한 디자인이 바뀌게 됩니다. 그 값에 대한 책임이 클라이언트에게 있는가 아니면 preTDD 에 있는가 하는 디자인이 바뀌
는 것이죠. 단순히 파라메터가 늘어난 것은 아니라고 보여집니다. 그 변수의 값에 대한 책임이 preTDD 에 있다고 보시면 파라
메터로 빼면 안되는 것이 맞고 preTDD 클래스는 그 변수의 값만 사용만 하면 된다고 보시면 클라이언트에서 주입을 해 주는 것
이 맞겠죠. 단일 책임 원칙 (Single Responsibility Principle)에 대해서 생각을 해 볼 수 있는 부분인
것 같습니다. 저의 경우 많은 경우 불필요한 파라메터의 증가라기 보다는 종속성이 감소 하는 현상을 경험하곤 합니다.

2번에 대해서는 항상 말이 많은 부분입니다. 저의 견해는 private 의 경우 public 에서 사용이 되므로 굳이 테스트를
위해 public 으로 열 이유는 없다고 봅니다. 보통 private 이라면 해당 클래스의 public 메소드에 지원을 해주는
helper 의 성격이 강하므로 독립적으로 호출을 하는 것은 그다지 의미가 없다고 봅니다. 독립적인 호출이 의미가 있다면
public 으로 개방을 하는 것이 맞겠죠. 그래도 굳이 private 을 따로 테스트를 해야 마음이 놓이겠다 싶으면
reflection 을 사용해서 테스트를 합니다. 물론 언어차원에서 지원이 되어야 겠죠.

Sangpil Byun

unread,
Dec 7, 2009, 9:10:49 AM12/7/09
to xp...@googlegroups.com
안녕하세요 메일링 리스트에 메일을 쓰기는 처음이네요.
반갑습니다.^^
 
저도 TDD에 대해선 완전 초짜 수준이지만, 함수를 테스트할때 함수는 가능한 상태가 없도록(stateless) 하는게 아무래도 테스트 하기 쉽지 않을까요? 내부 상태가 변하는 값들은 파라메터로 하는게 좋다고 생각합니다. 상태 정보에 따라 함수가 다르게 동작한다면 아마 파라메터로 던져주는게 코드 읽기도 더 편하다고 생각합니다.
 
그리고 2번에 대한 내용인데 Java를 쓰신다면 default 제한자라는 것이 있습니다.
그냥
 
 void methodToTest(int isThisUsed) { ... }
 
이렇게 public, private, protected등 쓰지 않는 경우인데요 동일한 패키지에서만 접근이 가능하기 때문에 일반적으로 같은 패키지 상에 존재하는 테스트 코드에선 접근이 가능하고, 다른 곳에서는 접근이 불가능하도록 만드는 접근자 입니다.
 
저는 public으로 풀려선 안되는 메소드는 위의 접근자로 테스트 하곤 합니다.
조금이나마 도움이 되었으면 좋겠네요. 그럼 즐거운 밤 되세요~

2009/12/7 Jake Kim <drca...@gmail.com>

손태원-핫메일

unread,
Dec 7, 2009, 7:27:53 PM12/7/09
to xp...@googlegroups.com

아시아나IDT 손태원입니다.

 

1.     내부 변수 의존성 문제

A.     테스트를 위해 내부 변수를 다시 파라미터로 전달할 필요는 없다고 생각합니다

B.      내부 변수를 설정하는 함수를 이용해서 테스트를 통제하는 것이 좋겠습니다. 그러면 내부변수 변화에 따른 테스트 결과 의존성을 통제할 수 있다고 봅니다.

C.      그리고 언어환경이 무엇인지는 모르지만, 테스트 함수를 제품 클래스에 넣는 것도 이상하군요. 그게 아니라면….

2.     Public 남용 문제

A.     테스트 코드 작성 전에 Private였는데 테스트를 위해 public으로 전환했다면 확실히 문제라고 보여집니다.

B.      테스트 용이성은 테스트 코드 자체만으로는 향상되지 않는다고 보아야 합니다.

C.      테스트 코드를 잘 작성하는 훈련도 필요하지만, 근본적으로 제품 코드의 설계가 테스트 용이하지 않는 경우가 많을 것이라고 보여집니다.

D.     따라서 제품 코드가 테스트 용이하지 않다면, 리팩터링을 선행해야 하는지 검토해보시기를 권해드립니다.

E.      그리고 테스트는 클래스 내부의 모든 함수와 기능을 테스트 하는 것이 아니라고 알고 있습니다.

F.      외부에 노출되고 사용되는 의미 있는 Behavior에 대해서 테스트하는 것이 단위테스트이니 테스트 코드를 과도하게 작성하려는지 살펴보아야겠습니다.

 

 TDD 실전 경험은 그리 많지 없지만 학습하고 이해한 바에 따라 의견을 회신드립니다. 혹시 작성하신 대상 코드를 가지고 실제로 토론을 해도 된다면 저로도서 많은 도움이 될 것 같습니다.

아샬

unread,
Dec 7, 2009, 8:52:01 PM12/7/09
to xp...@googlegroups.com
1. 멤버 변수 사용을 최소화하는 쪽이 독립적인 로직을 작성하기 좋다고 봅니다.

추가적으로 원래의 질문에 나온 코드를 파고들어 급진적으로 질문한다면,

정말로 if 분기가 필요할까요?

2. 외부에서 사용하지 않는 메서드는 사실상 사용하지 않는 메서드와 동일하다고 봅니다.

private 메서드가 필요하다면, 정말로 private으로 만들려는 이유를 찾아

해당 책임을 작은 클래스 하나에 위임하는 게 좋다고 봅니다.

TDD에서 경험하는 것과 단위 테스팅을 혼동하는 케이스가 보이는 것 같은데

저는 두가지를 명백히 구분해 설계 개선에 도움을 주는 게 유용한 전략이라고 봅니다.


2009년 12월 8일 오전 9:27, 손태원-핫메일 <ldst...@hotmail.com>님의 말:

ChungHee Lee

unread,
Dec 7, 2009, 9:54:08 PM12/7/09
to xp...@googlegroups.com
많은 분들의 조언 감사드립니다...(__)
private method test 관련해서 많은 이야기가
이미 2004년에 오갔다는 사실에 놀라기도 했구요... 
 
1. private 메소드를 테스트해야 하는 경우에는
클래스를 나눈 후, private 메소드를 public으로 선언한 후에 테스트를 해야 겠다는 생각이 듭니다.
 
사실 지금 설계에서는 메소드들이 하나의 클래스에 모여 있습니다.
클래스 크기가 그리 크지는 않지만, 용도별로 쪼개야 겠다는 생각이 드네요.
설계 시, private 메소드는 최소화 해야 겠습니다.
 
2. 창준님께서 실제 사용되는 코드 예를 보여주시면 조언을 해 주시겠다고 하셔서...
유닛테스트를 위하여 파라미터를 추가했던 메소드만을 발췌하여 첨부합니다.
(기타 메소드 및 멤버 변수들은 삭제하였습니다.)
 
코드 관련해서 하나 예를 들면,
isAlreadyDownloadedContent 메소드의 contentLength , downloadStartPosition 같은 변수는 클래스에 멤버로 저장되어 있는 변수라서
애초에는 파라미터로 넣지 않았습니다.
유닛테스트를 위해서 파라미터를 넣어 준 것이구요...
 
창준님 감사합니다. (__)
 
3. 너무 받기만 한 것 같아서, 자료를 찾다가 얻은 내용을 공유합니다.(다 아시는 내용일수도 있겠지만요 ^^;)
 
http://xper.org/wiki/xp/TestingPrivateInterfaces 내용 중에 PrivilegedAccessor 관련 내용이 있어서 자료를 찾는 도중에
다음 링크가 많은 도움이 되었습니다.
 
 java.lang.class의 getDeclaredFields() 메소드를 통해서 선언된 필드의 정보를 알아 내고(private 포함),
 java.lang.class.Field 클래스의 setAccessible 메소드(실제로는 AccessibleObject 에서 상속된 것임)를 사용하여 private 멤버의 값을 얻어내거나 변경하는 일이 가능하다는 내용입니다.
 멤버 뿐 아니라 메소드에도 접근 가능한 것 같구요...
 요걸 사용해서 JUnit에서 private 멤버(or 메소드) 테스트도 가능하다는 내용입니다.
 
 (최소한 java에서는) private 키워드는 절대적으로 보장되지 않는다는 생각이 들었고,
 이렇게까지 해서 private 멤버를 테스트하느니, 차라리 설계를 바꾸고 말지-_-;; 하는 생각도 들었습니다.
 감사합니다.
 점심 맛있게 드세요 ^^
2009년 12월 8일 오전 10:52, 아샬 <ahas...@gmail.com>님의 말:
Downloader.java

주넥

unread,
Dec 8, 2009, 8:42:14 PM12/8/09
to xper

안녕하세요.
아이디스 허준혁입니다.
최근에 실무가 바쁘다 보니 mailing list 를 따라가고 있지 못하다
오랫만에 글을 남기게 되었습니다.

최근에 저는 TDD라기 보다 기존에 작성된 코드 중에 UnitTest를 붙이는 것을
많이 해보고 있습니다.

설계상의 옳고 그름을 떠나 기존 코드를 변경하지 않고 UnitTest를 붙일 수는 있습니다.

1. 내부변수 사용의 경우
Test해야할 class를 상속받아서 Mock class를 만들고 Mock class 에서 내부 변수를 셋팅하는 함
수를 만들고,
Mock Class를 test하면 되겠지요. 그럼 기존 class에는 전혀 손대지 않고 UnitTesting가능하
게 됩니다.

2. Public method의 남발
Public이 싫으시면 가급적 protected로 사용하시고 역시 Mock Class를 이용해서 public 상속
을 받아서,
Mock Class를 test하면 됩니다.

제가 기존 코드를 변경하지 않고 UnitTest를 붙이는 것을 언급한 것은
최근에 제가 이런류의 작업을 많이 하고 있어서 입니다.

저희 회사의 경우 처음부터 TDD로 작성되지 않았으나, 재사용이 상당부분 되어
지속적으로 수정되고 있는 Legacy 코드들이 많고 이런 쪽에 문제점이 지속 발견되다보니,
UnitTest를 이미 짜져있는 코드에 많이 넣을 필요성이 있더군요.

기존 코드에 손을 대지 않고 UnitTest를 넣는 것이 쉬운 부분이 아니더군요.
특히 앞선 예와 같이 내부 state를 가지지 않는 function들은 쉽게 UnitTest가 붙을 수 있으나,
내부 state를 복잡하게 많이 참조하는 것들의 경우는 UnitTest 붙이는 것이 쉽지만은 않습니다.

물론 testable하게 refactoring하면 좋겠지만,
기존 코드에 UnitTest가 이미 많이 작성되어 있으면 refactoring또한 두려움이 없어지나,
그렇지 않기 때문에 refctoring또한 side-effect의 두려움으로 현장에서는 쉽지만은 않은 작업입니다.

이와 관련해서 읽어볼만한 책을 추천합니다.
"Working Effectively With Legacy Code" by Michael C. Feathers from
PRENTICE HALL

주넥

unread,
Dec 8, 2009, 8:53:21 PM12/8/09
to xper
>
> 2. Public method의 남발
> Public이 싫으시면 가급적 protected로 사용하시고 역시 Mock Class를 이용해서 public 상속
> 을 받아서,
> Mock Class를 test하면 됩니다.
>

Public의 남발의 경우 대신 protected를 사용하면
JAVA가 아니라 C++ 관련입니다만,
GoogleTest에서는 FRIEND_TEST() 라는 macro를 지원해줘서 test case에 대한
friend ship을 이용해서 처리할 수 있습니다.

저의 회사의 경우 UnitTest++ framework을 사용하다 여러 이점 때문에 최근에 GoogleTest로 옮겼탓습니다.
간단한 python script으로 기존 UnitTest++ 로 작성된 코드를 전부 GoogleTest로 쉽게 옮겨서,
생각만큼 Framework 옮기는 것의 overhead는 커지 않은 듯 합니다.

June Kim

unread,
Dec 9, 2009, 11:53:58 PM12/9/09
to xp...@googlegroups.com
코드 잘 봤습니다. 그런데 단위 테스트를 위해 파라미터를 추가한 메소드들만 보내주셔서 전체적인 평가가 어렵네요. 저는 전체적인
그림을 보고 싶었거든요.

여하튼 지금 갖고 있는 정보에서 말씀드리면,

설계를 바꿔서 해결하는 방법으로는 다음 두가지 방법이 생각이 나네요.
1) Downloader를 몇 개의 class로 쪼갠다 (coherent한 놈들끼리 묶어서 -- 이 경우 새로 생긴
클래스들의 메소드를 테스트함)
2) Downloader를 생성하기 쉽게 "가볍게" 만든다. DI 사용. (이 경우 파라미터를 새로 추가하지 않고 테스트)

인스턴스 변수랑 메소드 파라미터랑 둘 다 중복으로 있는 것은 리팩토링 대상이라고 생각합니다.

그리고 Downloader라는 이름에 대해. http://xper.org/wiki/seminar/OOP_c0_fb 참고하세요.

테스트하기 어려운 것은 대부분 설계 문제입니다.

2009/12/8 ChungHee Lee <dre...@gmail.com>:

ChungHee Lee

unread,
Dec 16, 2009, 1:01:54 AM12/16/09
to xp...@googlegroups.com
김창준님, 감사합니다. ^^
감사인사를 조금 늦게 드렸네요...
 
위의 Downloader 라는 이름과 관련해서 보내 주신 링크의 내용을 읽어 보고 코드를 봤더니
-er 로 끝나는 클래스명이 상당히 많네요..
XXXParser, YYYParser, Downloader, ZZZManager 등등...
 
링크의 글을 보고 생각해 보니
확실히 -er 이 붙으면 '객체' 라는 생각보다는 '무슨무슨 기능을 해 주는 도구' 로 생각하게 되는 경향이 있는 것 같습니다.
 
초기화 관련된 클래스를 하나 만들려고 하는데,
원래는 Initializer 라고 만들려고 하였지만, Initialization 으로 이름을 바꾸어 보려고 합니다.
 
그리고 Initialization 클래스의 크기가 작더라도, 클래스를 여러 개로 쪼개어
각각의 클래스를 test 해 보려고 합니다.
 
많은 생각을 하게 해주신 분들께 다시 한번 감사드립니다. ^^
즐거운 연말 되세요~
2009년 12월 10일 오후 1:53, June Kim <june...@gmail.com>님의 말:
--~--~---------~--~----~------------~-------~--~----~
한국 XP 사용자 모임(http://xper.org) 메일링 리스트에 가입하셨기에 이 메시지를 보내드립니다. Google 그룹스 "xper" 그룹
이 그룹에 게시하려면 다음 주소로 이메일을 보내주십시오.
xp...@googlegroups.com
이 그룹에서 탈퇴하시려면 다음으로 이메일을 보내주십시오.
xper+uns...@googlegroups.com
추가 옵션을 보려면 http://groups.google.com/group/xper?hl=ko?hl=ko의 그
룹을 방문하십시오.
-~----------~----~----~----~------~----~------~--~---


Reply all
Reply to author
Forward
0 new messages