메인 코드를 만들기 전에 해당 코드의 의도를 표현해줄 테스트 코드를 먼저 만드는 기법. ---- 어떻게 보면 질답법과도 같다. 프로그래머는 일단 자신이 만들려고 하는 부분에 대해 질문을 내리고, TestCase를 먼저 만들어 냄으로서 의도를 표현한다. 이렇게 UnitTest Code를 먼저 만듬으로서 UnitTest FrameWork와 컴파일러에게 내가 본래 만들고자 하는 기능과 현재 만들어지고 있는 코드가 하는일이 일치하는지에 대해 어느정도 디버깅될 정보를 등록해놓는다. 이로서 컴파일러는 언어의 문법에러 검증뿐만 아니라 알고리즘 자체에 대한 디버깅기능을 어느정도 수행해주게 된다. 테스트코드는 프로그래머가 하려고 하는일, 즉 의도를 담아낸다. 이는 이 프로그램이 어떠한 시나리오로 돌아갈것인가를 먼저 생각해보는 기회를 저절로 제공해준다. Test가 가능한 코드는 run 을 시켰을때 어떤 결과를 낼지를 파악할 수 있는 코드이다. 이 경우 해당 모듈이 완성되었을때가 언제인지 그 목표를 분명하게 잡는 역할을 해준다. 테스트코드 자체가 일종의 도큐먼트역할을 하기도 한다. 테스트 코드를 만들면서 자신이 하려는 일과 문제상황을 구체화 시켜간다. 테스트코드가 완벽할 순 없다. 하지만, 테스트코드가 모든 에러를 잡아내지 못한다는 이유로 많은 버그들을 줄일 수 있는 테스트코드를 작성하지 않을 이유는 없다. ExtremeProgramming에서는 UnitTest -> Coding -> ["Refactoring"] 이 맞물려 돌아간다. TestFirstProgramming 과 ["Refactoring"] 으로 단순한 디자인이 유도되어진다. 요새는 ["TestDrivenDevelopment"] 라고 한다. 단순히 Test 를 먼저 작성하는게 아닌, Test 주도 개발인 것이다. TestDrivenDevelopment 는 제 2의 Refactoring 과도 같다고 생각. --["1002"] ex) ["TFP예제/Omok"], ["TFP예제/Queue"], ["TFP예제/WikiPageGather"] 참조 사이트 : * wiki:Wiki:CodeUnitTestFirst, wiki:Wiki:TestFirstDesign, wiki:Wiki:TestDrivenProgramming * wiki:NoSmok:TestFirstProgramming * wiki:Wiki:ExtremeProgrammingUnitTestingApproach 테스트 코드 작성에 대해서는 UnitTest 와 PyUnit, CppUnit 를 참조하라. === Test Code Refactoring === 프로그램이 길어지다보면 Test Code 또한 같이 길어지게 된다. 어느정도 Test Code 가 길어질 경우에는 새 기능에 대한 테스트코드를 작성하려고 할 때마다 중복이 일어난다. 이 경우에는 Test Code 를 ["Refactoring"] 해야 하는데, 이 경우 자칫하면 테스트 코드의 의도를 흐트려뜨릴 수 있다. 테스트 코드 자체가 하나의 다큐먼트가 되므로, 해당 테스트코드의 의도는 분명하게 남도록 ["Refactoring"] 을 해야 한다. * wiki:Wiki:RefactoringTestCode === Test - Code Cycle === 테스트를 작성하는 때와 Code 를 작성하는 때의 주기가 길어질수록 힘들다. 주기가 너무 길어졌다고 생각되면 다음을 명심하라. wiki:Wiki:DoTheSimplestThingThatCouldPossiblyWork === Test Code Approach === Test Driven 에 대한 접근 방법에는 End-To-End (BlackBoxTesting) 식의 접근 방법과 WhiteBoxTesting 의 접근방법이 있을 수 있겠다. 전자의 경우는 일종의 '부분결과 - 부분결과' 를 이어나가면서 최종목표로 접근하는 방법이다. 이는 어떻게 보면 Functional Approach 와 유사하다. (Context Diagram 을 기준으로 계속 Divide & Conquer 해 나가면서 가장 작은 모듈들을 추출해내고, 그 모듈들을 하나하나씩 정복해나가는 방법) 후자의 경우는 해당 코드의 구조를 테스트해나가는 방법으로, 해당 코드의 진행이 의도한 상황에 맞게 진행되어가는지를 체크해나가는 방법이다. 이는 MockObjects 를 이용하여 접근할 수 있다. 즉, 해당 테스트하려는 모듈을 MockObject로 구현하고, 호출되기 원하는 함수들이 제대로 호출되었는지를 (MockObjects 의 mockobject.py 에 있는 ExpectationCounter 등의 이용) 확인하거나 해당 데이터의 추가 & 삭제관련 함수들이 제대로 호출되었는지를 확인하는 방법 (ExpectationList, Set, Map 등의 이용) 등으로서 접근해 나갈 수 있다. Test - Code 주기가 길다고 생각되거나, 테스트 가능한 경우에 대한 아이디어가 떠오르지 않은 경우, 접근 방법을 다르게 가져보는 것도 하나의 방법이 될 수 있겠다. === 시나리오의 이용 === Test Code 를 작성하진 않았지만, 이런 경험은 있었다. PairProgramming 을 하는 중 파트너에게 '이번에는 Socket Class 를 만들 차례야. 시작해볼까' 파트너가 먼저 코드를 잡긴 했는데, 코드가 좀처럼 진행이 되지 않았다. 문제가 뭘까 고민하다가 다음과 같이 접근해봤다. '지금 저쪽에는 에코서버가 있어. 지금 만들건 클라이언트고, 지금 만들 클래스로 write 를 하면 저기 띄어놓은 에코서버에 내가 입력한 메세지가 그대로 표시될거야' 즉, 완성되었을 때의 결과를 미리 그려보는 것이다. 아까보다 훨씬 수월하게 진행되었고, 그 결과를 눈으로 확인했고, 결과를 눈으로 확인한뒤 '완료' 했다. TFP 라면 이를 코드로 작성하여 자동화 할 것이다. 자동화된 테스트는 앞으로의 추후 모듈 수정시에도 앞에서 내가 원하는 기능들이 여전히 작동함을 보장해준다. === Random Generator === Random 은 우리가 예측할 수 없는 값이다. 이를 처음부터 테스트를 하려고 하는 것은 좋은 접근이 되지 못한다. 이 경우에는 Random Generator 를 ["MockObjects"] 로 구현하여 예측 가능한 Random 값이 나오도록 한 뒤, 테스트를 할 수 있겠다. === Server - Client === 이 경우에도 ["MockObjects"] 를 이용할 수 있다. 기본적으로 XP에서의 테스트는 자동화된 테스트, 즉 테스트가 코드화 된 것이다. 처음 바로 접근이 힘들다면 Mock Server / Mock Client 를 만들어서 테스트 할 수 있겠다. 즉, 해당 상황에 대해 이미 내장되어 있는 값을 리턴해주는 서버나 클라이언트를 만드는 것이다. (이는 TestFirstProgramming 에서보단 ["AcceptanceTest"] 에 넣는게 더 맞을 듯 하긴 하다. XP 에서는 UnitTest 와 AcceptanceTest 둘 다 이용한다.) === Thread === TddRecursiveDescentParsing ---- ["ExtremeProgramming"]