테스트 주도 개발
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.
1. 개요
테스트 주도 개발(TDD)은 켄트 벡에 의해 "재발견"된 소프트웨어 개발 방법론으로, 실패하는 테스트를 먼저 작성하고 이를 통과하는 최소한의 코드를 구현하는 방식으로 진행된다. TDD는 'Red-Green-Refactor' 사이클을 반복하며, 테스트 작성, 코드 작성, 리팩토링 단계를 거친다. 이 방법론은 코드의 품질, 설계, 유지보수성을 향상시키고, 개발자의 생산성을 높이는 장점이 있지만, 코드 양 증가, 테스트 유지 관리의 어려움, 학습 곡선 등의 단점도 존재한다. TDD는 보안 소프트웨어, 병렬 처리, GUI 등 특정 상황에서는 적용이 어려울 수 있으며, 테스트 가능하도록 설계를 하고, 적절한 테스트 범위를 선정하며, 지속적인 학습과 개선을 통해 그 효과를 높일 수 있다.
더 읽어볼만한 페이지
- 익스트림 프로그래밍 - 워드 커닝햄
워드 커닝햄은 미국의 컴퓨터 프로그래머로, 최초의 위키 사이트 WikiWikiWeb을 만들고 기술 부채 개념을 창안했으며, 소프트웨어 개발 방법론 발전에 기여했다. - 익스트림 프로그래밍 - JUnit
JUnit은 자바 환경에서 단위 테스트를 위한 프레임워크로, 반복적인 테스트 실행을 통해 버그 수정에 용이하며, 어노테이션 기반의 간편한 테스트 코드 작성과 IDE 통합을 지원하여 개발 효율성을 높인다. - 소프트웨어 개발 철학 - 애자일 소프트웨어 개발
애자일 소프트웨어 개발은 1990년대에 등장하여 개인과 상호작용, 작동하는 소프트웨어, 고객과의 협력, 변화에 대한 대응을 핵심 가치로 삼고 적응형 계획과 반복적 실행을 통해 시장 출시 속도와 위험 완화를 추구하는 소프트웨어 개발 방법론이다. - 소프트웨어 개발 철학 - 유닉스 철학
유닉스 철학은 단순성, 모듈성, 재사용성을 중시하며, 한 가지 일을 잘하는 프로그램, 협력적인 프로그램, 텍스트 스트림 처리 프로그램을 지향하는 소프트웨어 설계 원칙으로, 현대 운영체제 및 소프트웨어 설계에 영향을 주지만 비판도 존재한다. - 소프트웨어 개발 프로세스 - 버전 관리
버전 관리는 파일 변경 이력을 체계적으로 관리하는 시스템이며, 다양한 구조와 소스 관리 모델을 통해 협업을 지원하고, 비즈니스 등 다양한 분야에서 활용된다. - 소프트웨어 개발 프로세스 - 소프트웨어 개발 수명 주기
소프트웨어 개발 수명 주기(SDLC)는 시스템 설계자와 개발자가 따르는 일련의 단계로, 예비 분석부터 폐기까지 여러 단계를 거치며, 폭포수 모델, 시스템 분석 및 설계(SAD), 객체 지향 분석 및 설계(OOAD) 등 다양한 방법론을 포함한다.
테스트 주도 개발 | |
---|---|
개요 | |
![]() | |
유형 | 소프트웨어 개발 방법론 |
지지자 | 켄트 백 제프리 팔머 론 제프리스 |
반대자 | 바즈 베 |
다른 이름 | ATDD (인수 테스트 주도 개발) Behavior-Driven Development (행위 주도 개발) |
세부 정보 | |
설명 | 소프트웨어 개발 프로세스 |
방법 | 테스트 작성 최소한의 코드 작성 리팩토링 |
장점 | 명확한 요구사항 단순한 디자인 높은 신뢰도 |
단점 | 시간 소요 학습 곡선 테스트 유지보수 |
역사 | |
기원 | 스몰토크 및 익스트림 프로그래밍 |
영향 | 행위 주도 개발 인수 테스트 주도 개발 |
주요 개념 | |
적색-녹색-리팩토링 주기 | 적색: 실패하는 테스트 작성 녹색: 테스트를 통과하는 최소한의 코드 작성 리팩토링: 코드 개선 |
테스트 우선 | 코드 작성 전에 테스트 작성 |
짧은 주기 | 짧은 간격으로 반복적인 개발 |
적용 분야 | |
일반적인 사용 사례 | 단위 테스트 통합 테스트 시스템 테스트 |
적용 가능성 | 다양한 프로그래밍 언어 및 프레임워크 |
참고 자료 | |
관련 주제 | 테스트 자동화 지속적인 통합 리팩토링 |
표준 | 없음 (개발자의 재량에 따라 다름) |
2. 역사
켄트 벡은 이 기법을 개발하거나 "재발견"한 것으로 알려져 있으며[5], 2003년에 TDD가 단순한 설계를 장려하고 자신감을 불어넣는다고 말했다.[7] 켄트 벡은 TDD를 "재발견"으로 칭하며, TDD에 대한 설명은 오래된 프로그래밍 책에도 있었다고 언급했다. 그는 스몰토크에서 최초의 xUnit 프레임워크를 작성한 후, 입력과 예상 출력을 비교하는 방식을 시도했고, 이것이 TDD의 시작이었다고 밝혔다. 그는 TDD를 설명할 때, 나이 많은 프로그래머들로부터 "다른 방식으로 프로그래밍할 수 있겠어요?"라는 반응을 자주 듣는다고 한다.[6]
3. 개발 주기
TDD는 'Red-Green-Refactor'라고 불리는 짧은 개발 주기를 반복한다. 각 주기는 xUnit이라는 테스트 실행 환경 도구를 통해 테스트의 실패(빨간색)와 성공(녹색)을 시각적으로 보여주며 진행된다.[8]
개발 주기 (Red-Green-Refactor)TDD 개발 주기는 테스트 작성(Red), 코드 작성(Green), 리팩토링(Refactor)의 세 단계를 거치며, 필요에 따라 반복된다.
각 단계는 더 세부적인 작업으로 나눌 수 있다. 먼저 테스트할 항목을 목록으로 작성하고, 구현 가능하고 중요한 항목을 선택하여 테스트를 작성한다. 그 후 테스트를 통과하는 코드를 작성하고, 코드와 테스트의 중복을 제거하는 리팩토링을 수행한다. 이 과정을 반복하여 기능을 구현한다.
TDD를 위해서는 JUnit이나 NUnit과 같은 자동화된 테스트 실행 환경(xUnit)이 필요하다.[3]
3. 1. 테스트 작성 (Red)
개발자는 구현해야 할 기능에 대한 요구사항을 명확히 정의하고, 이를 검증하는 실패하는 테스트 케이스를 작성한다. 이 테스트는 새로운 기능이 아직 구현되지 않았기 때문에 실패해야 한다 ('Red' 단계).[8] 새로운 동작에서 예상되는 변형을 나열하는데, 이때 사용 사례와 사용자 스토리를 참고하여 요구사항을 발견할 수 있다. 이는 개발자가 코드를 작성하기 *전에* 요구사항에 집중하게 한다는 장점이 있다.[8]
테스트 작성(Red) 단계는 일반적으로 다음과 같다.
1. 새로운 동작의 변형이 충족되면 통과할 *수 있는* 자동화된 테스트를 작성한다.[8]
2. 모든 테스트를 실행한다. 새로운 테스트는 *실패*해야 한다. 이는 원하는 기능에 실제로 새로운 코드가 필요하다는 것을 보여준다. 또한, 테스트 하네스가 올바르게 작동하는지 검증하고, 새로운 테스트가 결함이 있어 항상 통과할 가능성을 배제한다.[8]
한국의 개발 문화에서는 요구사항 명세서, 사용자 스토리 등을 활용하여 테스트 케이스를 도출하는 경우가 많다.
3. 2. 코드 작성 (Green)
켄트 백이 개발하거나 "재발견"한 테스트 주도 개발(TDD)에서, 개발자는 가능한 한 빨리 테스트를 통과하는 가장 간단한 코드를 작성한다. 이 단계를 'Green' 단계라고 부르며, 코드의 완벽성보다는 테스트 통과를 우선시한다.[7]
이 단계에서는 'Fake It'(가짜 구현) 전략을 사용하기도 하는데, 이는 하드 코딩된 값을 반환하는 임시 코드를 작성하는 것을 의미한다. 이를 통해 개발자는 빠르게 테스트를 통과시키고 다음 단계로 넘어갈 수 있다.
xUnit에서는 테스트 통과를 녹색 막대로 표시하기 때문에, 이 단계를 'Green'이라고 부른다. 구체적인 방법은 다음과 같다.[8]
이러한 방법을 통해 개발자는 빠르게 테스트를 통과하는 코드를 작성하고, 이후 리팩토링 단계를 통해 코드의 품질을 향상시킨다.
3. 3. 리팩토링 (Refactor)
코드는 코드 리팩터링을 통해 코드 가독성과 유지 관리성을 확보한다. 특히, 하드 코딩된 테스트 데이터는 프로덕션 코드에서 제거해야 한다.[7] 각 리팩토링 후에는 테스트 스위트를 실행하여 기존 기능이 손상되지 않았는지 확인한다.
리팩토링의 예시는 다음과 같다:[7]
일반적으로 리팩토링이란, 코드의 의미를 변경하지 않고 코드를 재구성하는 것을 말한다. 여기서 "코드의 의미"란, 테스트가 통과하는 것을 말한다. 또한, 제거하는 중복에는 형식적인 중복뿐만 아니라, 의미적인 중복도 포함된다.[8]
3. 4. 반복
개발자는 필요한 모든 기능이 구현될 때까지 위 단계를 반복한다. 각 주기는 짧게 유지되며, 지속적인 통합을 통해 코드 변경 사항을 자주 통합하고 테스트한다.[3]
각 테스트는 작아야 하며, 자주 커밋해야 한다. 새로운 코드가 일부 테스트에 실패하면 프로그래머는 과도하게 디버깅하는 대신 실행 취소하거나 되돌릴 수 있다.[3]
테스트 실행 환경 도구인 xUnit에서는, 테스트의 실패를 빨간색 막대, 성공을 녹색 막대로 알리기 때문에, 가장 기본적인 개발 사이클은 Red/Green/Refactor라고 불린다.
더욱 실용적으로는 할 일 목록을 조합하여 다음과 같은 순서로 개발한다.
테스트 코드는 처음부터 자명한 것은 아니다. 오히려 코드 본체와 마찬가지로, 처음에는 구체적인 테스트(예를 들어, 단순한 플래그의 확인)를 수행하고, 이것에 의해 지견을 얻은 후에 테스트를 다시 작성하는 편이 좋다. 또한, 테스트 코드에서 유도되는 코드 본체는 리팩토링 과정에 의해서, 혹은 테스트가 성숙함에 따라, 최종적인 목적으로 하는 코드 본체의 테스트용 스텁으로 바뀔 수도 있다. 빠른 단계에서 테스트와 코드 본체를 분리해서 관리하는 것은 그다지 의미가 없다. 테스트나 코드 본체가 성숙해 감에 따라, 테스트의 작성이 추상적, 간접적이 되고, 리스크가 도입된다(예를 들어, 필드를 직접 참조하는 대신 getter 메서드를 사용하는 등). 그러나, 테스트 주도 개발의 테스트의 목적은 개발자의 올바름에 대한 확신을 뒷받침하기 위한 것이며, 그것이 보장되고 있다면 문제는 없다.
테스트 주도 개발을 실시하기 위해서는 테스트를 자동으로 실행할 수 있는 환경이 필요하다. 그러한 환경으로는 JUnit이나 NUnit과 같은 것(총칭해서 xUnit라고 불린다)이 있다. 이 테스트 실행 환경은 컨셉이 단순하고, 게다가 매우 강력하기 때문에, 실행 환경 자체를 테스트 주도 개발로 자작하는 것도 좋다. 다만, 그 테스트 도구를 테스트하는 도구는 없기 때문에, 당분간은 신중한 사람의 판단으로 테스트를 대신하게 된다.
4. TDD와 다른 개발 방법론
TDD는 코드 단위(함수, 클래스, 모듈)가 올바르게 작동하는지 확인하는 개발자 중심의 방법론이다. 반면, 인수 테스트 주도 개발(ATDD)은 고객, 개발자, 테스터 간의 소통을 통해 요구 사항을 명확히 하는 데 사용된다.[20] TDD는 테스트 자동화가 필수적이지만, ATDD는 자동화가 필수는 아니다. 하지만 ATDD에서 자동화를 하면 회귀 테스트에 도움이 된다. TDD 테스트는 ATDD 테스트에서 파생될 수 있는데, 코드 단위가 요구 사항의 일부를 구현하기 때문이다.
행위 주도 개발(BDD)은 TDD와 ATDD를 결합한 것이다.[21] BDD는 테스트를 먼저 작성하지만, 코드 구현 단위보다는 행위 자체를 설명하는 테스트에 중점을 둔다. JBehave, Cucumber, Mspec 등의 도구를 사용하여 제품 소유자, 개발자, 테스터가 함께 정의한 내용을 자동화된 테스트로 만들 수 있다.
4. 1. 인수 테스트 주도 개발 (ATDD)
인수 테스트 주도 개발(ATDD)은 고객, 개발자, 테스터 간의 의사 소통 도구로, 요구 사항이 잘 정의되었는지 확인하는 데 사용된다.[20] 반면, TDD는 주로 개발자가 코드 단위(함수, 클래스, 모듈)가 일련의 작업을 올바르게 수행하는지 확인하는 데 도움을 주는 도구이다. TDD는 테스트 자동화가 필요하지만, ATDD는 필수는 아니다. 그러나 ATDD에서 자동화는 회귀 테스트에 도움이 된다. TDD에 사용되는 테스트는 ATDD 테스트에서 파생되는 경우가 많은데, 이는 코드 단위가 요구 사항의 일부를 구현하기 때문이다. ATDD 테스트는 고객이 읽을 수 있어야 하지만, TDD 테스트는 그럴 필요가 없다.4. 2. 행위 주도 개발 (BDD)
행위 주도 개발(BDD)은 테스트 주도 개발(TDD)과 인수 테스트 주도 개발(ATDD)의 실천 방식을 결합한 것이다.[21] BDD는 테스트를 먼저 작성하지만, 구현 단위보다는 행위를 설명하는 테스트에 초점을 맞춘다. JBehave, Cucumber, Mspec, Specflow와 같은 도구를 사용하면 제품 소유자, 개발자, 테스트 엔지니어가 함께 정의한 구문을 자동화된 테스트로 변환할 수 있다.5. TDD 도구
TDD를 효과적으로 수행하기 위해 다양한 자동화된 테스트 프레임워크와 도구가 사용된다.
5. 1. xUnit 프레임워크
개발자들은 테스팅 프레임워크를 컴퓨터를 이용해 사용할 수 있는데, 이를 통칭하여 1998년에 만들어진 SUnit에서 파생된 xUnit이라고 부른다. xUnit 프레임워크는 단언 스타일의 테스트 유효성 검사 기능과 결과 보고 기능을 제공한다. 이러한 기능은 자동화에 매우 중요한데, 실행 유효성 검사의 부담을 독립적인 사후 처리 활동에서 테스트 실행에 포함되는 활동으로 옮기기 때문이다. 이러한 테스트 프레임워크에서 제공하는 실행 프레임워크를 통해 모든 시스템 테스트 케이스 또는 다양한 하위 집합을 다른 기능과 함께 자동으로 실행할 수 있다.[22]테스팅 프레임워크는 1987년에 만들어진 언어 독립적인 Test Anything Protocol(TAP) 형식의 유닛 테스트 출력을 받아들일 수 있다.
6. TDD의 장단점
TDD는 코드 품질과 설계, 유지보수성 등 여러 면에서 장점을 제공한다.
하위 섹션 "장점"에서 언급된 것처럼, TDD는 코드의 신뢰성과 안정성을 높인다.[23] 또한, 테스트를 먼저 작성함으로써 요구 사항을 더 명확하게 이해하고, 코드의 목적을 명확하게 문서화할 수 있다.[23] 이는 개발자가 코드에 대해 더 깊이 생각하고, 더 나은 설계를 하도록 돕는다.
켄트 백은 TDD를 통해 "단순하게 유지하라"(KISS) 및 "당신은 그것을 필요로 하지 않을 것이다"(YAGNI)와 같은 원칙을 실천하여 더 깔끔하고 명확한 설계를 얻을 수 있다고 하였다.[7] TDD는 먼저 작동하는 코드를 작성한 후, 리팩토링을 통해 깨끗한 코드로 만드는 방식을 따른다.
하지만, TDD는 테스트 코드 작성으로 인해 코드 양이 늘어나고, 유지보수에 더 많은 시간과 노력이 필요할 수 있다는 단점도 있다.[24] 또한, 테스트 통과에만 집중하면 소프트웨어 설계의 큰 그림을 놓칠 수 있으며, 모든 유형의 테스트를 TDD로 대체할 수는 없다.[38]
6. 1. 장점
테스트 주도 개발(TDD)은 코드 품질을 높이고, 더 나은 설계를 유도하며, 빠른 피드백을 제공하여 유지보수성을 향상시키는 등 여러 장점을 제공한다.장점 | 설명 |
---|---|
포괄적인 테스트 커버리지 | 모든 새로운 코드가 최소한 하나의 테스트로 검증되도록 보장하여 소프트웨어의 견고성을 높인다.[23] |
코드 신뢰성 향상 | 개발자는 자신이 작성한 코드의 기능과 안정성에 대해 더 큰 확신을 가질 수 있다.[23] |
테스트 신뢰성 향상 | 구현이 올바르지 않으면 테스트가 실패하기 때문에, 테스트가 실제로 구현을 제대로 검증하고 있다는 것을 확신할 수 있다.[23] |
잘 문서화된 코드 | 각 테스트는 테스트 대상 코드의 목적을 명확하게 설명하므로, 자연스럽게 코드가 잘 문서화된다.[23] |
요구 사항 명확성 | TDD는 코딩을 시작하기 전에 요구 사항을 명확하게 이해하도록 돕는다.[23] |
지속적 통합 용이 | 지속적 통합 프로세스와 잘 연동되어 코드 업데이트 및 테스트를 자주 수행할 수 있다.[23] |
생산성 향상 | 많은 개발자들이 TDD가 생산성을 높인다고 보고한다.[23] 2005년의 한 연구에 따르면 TDD를 사용하면 더 많은 테스트를 작성하게 되고, 결과적으로 더 많은 테스트를 작성하는 프로그래머는 더 생산적인 경향을 보였다.[25] |
코드 멘탈 모델 강화 | TDD는 코드 구조와 동작에 대한 강력한 멘탈 모델을 구축하는 데 기여한다.[23] |
설계 및 기능 강조 | 프로그램의 설계, 인터페이스 및 전반적인 기능에 집중하도록 유도한다.[23] |
디버깅 필요성 감소 | 개발 초기 단계에서 문제를 발견함으로써 TDD는 나중에 광범위한 디버깅의 필요성을 줄여준다.[23] |
시스템 안정성 | TDD로 개발된 애플리케이션은 더 안정적이며 버그 발생 가능성이 낮다.[23] |
증가된 자신감 | TDD는 프로그래머가 자신감 있게 코드를 변경하거나 새로운 기능을 추가할 수 있도록 돕는다. 코드가 지속적으로 테스트된다는 것을 알면 기존 기능이 손상될 위험에 대한 두려움을 줄일 수 있다.[30] |
변경에 대한 두려움 감소, 스트레스 감소 | TDD는 포괄적인 테스트를 통해 코드 변경으로 인해 발생할 수 있는 문제를 즉시 발견할 수 있도록 하여, 코드 변경에 대한 두려움과 스트레스를 줄여준다.[30] |
향상된 집중력 | 테스트를 먼저 작성하면 프로그래머는 요구 사항과 디자인에 집중할 수 있어, 더 명확하고 목적 있는 코딩을 할 수 있게 된다. |
성취감 및 직무 만족도 | 테스트를 통과할 때마다 빠르고 정기적인 성취감을 얻을 수 있어 개발자의 사기를 높이고 직무 만족도를 향상시킬 수 있다. |
모듈화, 유연성, 확장성 | TDD는 더 모듈화되고, 유연하며, 확장 가능한 코드로 이어질 수 있다. 개발자가 소프트웨어를 독립적으로 테스트 가능한 작은 단위로 생각하도록 유도하기 때문이다. 이는 더 작고, 더 집중된 클래스, 더 느슨한 결합, 그리고 더 깨끗한 인터페이스로 이어진다. 모의 객체 디자인 패턴의 사용 또한 코드의 모듈화에 기여한다.[30] |
코드 커버리지 | TDD를 통해 작성된 자동화된 테스트는 모든 코드 경로를 커버하는 경향이 있다. TDD 개발자가 기존 `if` 문에 `else` 분기를 추가하려면, 먼저 해당 분기를 유발하는 실패하는 테스트 케이스를 작성해야 하기 때문이다. |
결함 감소 | TDD는 코드의 결함 수를 제한하는 데 도움이 된다. 테스트의 초기 및 빈번한 특성은 개발 주기 초기에 결함을 발견하여, 결함이 만연하고 비용이 많이 드는 문제가 되는 것을 방지하는 데 도움이 된다. |
실험적 증거 | Madeyski[30]는 200명 이상의 개발자를 대상으로 한 일련의 실험을 통해 객체 간의 낮은 결합(CBO)과 관련하여 TDD 방식이 전통적인 테스트-후 방식 또는 정확성 테스트 방식보다 우월하다는 실증적인 증거를 제시했다. |
켄트 백은 "단순하게 유지하라"(KISS) 및 "당신은 그것을 필요로 하지 않을 것이다"(YAGNI) 원칙과 같이 테스트를 통과하는 데 필요한 코드만 작성하는 데 집중함으로써 다른 방법보다 더 깔끔하고 명확한 설계를 얻을 수 있다고 하였다.[7]
"작동하는 클린 코드"가 테스트 주도 개발의 목표이다. 그러나 처음부터 깨끗하고 작동하는 코드를 작성하는 것은 어렵다. 그래서 테스트 주도 개발에서는 먼저 작동하는(테스트를 통과하는) 코드를 작성하고, 다음에 이를 리팩토링하여 깨끗한 코드로 정리한다.
6. 2. 단점
테스트 주도 개발(TDD)에는 다음과 같은 단점이 있다.- 코드 양 증가: TDD를 구현하면 테스트 코드가 추가되어 코드베이스가 커질 수 있다.
- 잘못된 보안 감각: 많은 수의 통과 테스트는 코드의 견고성에 대한 오해를 불러일으킬 수 있다.[24]
- 유지 관리 부담: 대규모 테스트 모음을 유지 관리하면 개발 프로세스에 부담이 될 수 있다.
- 시간 소모: 테스트를 작성하고 유지 관리하는 데 시간이 많이 소요될 수 있다.
- 테스팅 환경: TDD는 적절한 테스팅 환경을 설정하고 유지 관리해야 한다.
- 학습 곡선: TDD 방식을 능숙하게 익히는 데 시간과 노력이 필요하다.
- 과도한 복잡성: TDD를 지나치게 강조하면 필요 이상으로 복잡한 코드가 만들어질 수 있다.
- 전반적인 설계 무시: 테스트 통과에만 집중하면 소프트웨어 설계의 큰 그림을 놓칠 수 있다.
- 비용 증가: TDD에 필요한 추가 시간과 자원은 개발 비용 증가로 이어질 수 있다.
- 단위 테스트의 한계: TDD는 사용자 인터페이스, 데이터베이스와 함께 작동하는 프로그램, 특정 컴퓨터 네트워크 구성을 사용하는 프로그램 등에는 충분한 테스트를 제공하지 못할 수 있다.[38] 이러한 경우에는 완전한 기능 테스트가 필요하다.
- 경영진 지원 부족: 조직 전체가 TDD의 이점을 믿지 않으면 테스트 작성 시간을 낭비라고 생각할 수 있다.[40]
- 개발자와 테스트의 사각지대 공유: 테스트 작성자가 코드의 문제점을 인지하지 못하면 테스트도 같은 문제를 놓칠 수 있다.
- 잘못된 안전 의식: 많은 수의 통과된 단위 테스트는 잘못된 안전 의식을 유발하여, 통합 테스트 및 규정 준수 테스트와 같은 추가적인 소프트웨어 테스트를 소홀히 할 수 있다.
- 유지보수 부담: 잘못 작성된 테스트는 유지보수 비용을 증가시킬 수 있으며, 특히 취약한 테스트의 경우 더욱 그렇다.[41]
- 과도한 테스트 유지 관리 시간: 너무 많은 테스트를 작성하고 유지 관리하는 데는 시간이 걸린다.
- 테스트 커버리지의 잠재적 손실: 초기 테스트는 시간이 지남에 따라 중요해지는데, 부실한 설계 등으로 인해 많은 테스트가 실패할 때 이를 수정하지 않고 삭제하면 테스트 커버리지에 문제가 생길 수 있다.
7. TDD 적용 시 고려사항
TDD를 적용할 때 고려해야 할 사항은 다음과 같다.
테스트 가능하도록 설계객체 지향 설계에서는 비공개 데이터와 메서드에 직접 접근할 수 없다. 따라서 단위 테스트를 위해 추가 작업이 필요할 수 있다. 예를 들어, 자바에서는 리플렉션을 사용하여 비공개 멤버에 접근할 수 있다.[13] 또는 내부 클래스나 .NET의 부분 클래스를 사용하여 테스트에서 비공개 멤버에 접근 가능하게 할 수도 있다.
C/C++에서는 `#if DEBUG ... #endif` 같은 컴파일러 지시어를 사용하여 테스트 관련 코드가 릴리스 버전에 포함되지 않도록 할 수 있다.[13] 그러나 이는 릴리스 코드와 단위 테스트 코드가 정확히 동일하지 않음을 의미한다. 따라서 최종 릴리스 빌드에서 더 적지만 포괄적인 종단 간 통합 테스트를 정기적으로 실행하여, 테스트 도구에 의존하는 코드가 없도록 해야 한다.
비공개 메서드나 데이터 테스트의 필요성에 대해서는 논쟁이 있다. 일부는 비공개 멤버가 구현 세부 사항이므로 변경될 수 있어야 한다고 주장하며, 공용/보호된 인터페이스 테스트로 충분하다고 한다.[14] 반면, 핵심 기능이 비공개 메서드로 구현될 수 있으며, 직접 테스트가 더 작고 직접적인 단위 테스트의 이점을 제공한다는 주장도 있다.[15][16]
개발 중인 코드가 데이터베이스, 웹 서비스 등 외부 프로세스나 서비스에 의존하는 경우, 유닛 테스트 가능한 분리가 필요하다.[17] 이를 위해 다음 두 단계를 따른다.
1. 외부 액세스가 필요한 경우, 의존성 역전 원칙에 따라 인터페이스를 정의한다.
2. 인터페이스를 두 가지 방식(실제 외부 프로세스 접근, 가짜/모의 객체)으로 구현한다. 가짜 객체는 "Person 객체 저장됨" 같은 메시지를 추적 로그에 추가하여 동작을 확인한다.[17] 모의 객체는 자체 테스트 단언을 포함하여 더 정교한 검증을 할 수도 있다.
가짜나 모의 객체 메서드는 항상 동일하고 현실적인 데이터를 반환하여 테스트를 지원한다. 오류 처리 루틴 테스트를 위해 미리 정의된 오류 모드(유효하지 않은 응답, 예외 등)를 설정할 수도 있다. 가짜 암호화 서비스, 가짜 난수 서비스 등도 활용 가능하다. 이러한 방식은 의존성 주입의 예시이다.
테스트 더블은 UUT(Unit Under Test, 테스트 대상 유닛)가 의존하는 시스템 기능을 대체하는 테스트 특정 기능이다. 링크 시점 또는 실행 시점에 시스템에 도입될 수 있다. 링크 시점 대체는 테스트 더블을 로드 모듈로 컴파일하는 방식이다. 런타임 대체는 함수 포인터나 객체 대체를 통해 테스트 케이스 실행 중 실제 기능을 대체하는 방식이다.
테스트 더블의 유형 및 복잡성은 다음과 같다.
- 더미: 기본 반환 값을 제공하여 링크 시점 대체를 용이하게 한다.
- 스텁: 더미에 단순 로직을 추가하여 다른 출력을 제공한다.
- 스파이: 매개변수 및 상태 정보를 캡처하여 더 발전된 상태 유효성 검사를 허용한다.
- 모의: 테스트별 동작을 확인하기 위해 개별 테스트 케이스에 의해 지정된다.
- 시뮬레이터: 대상 기능에 대한 고충실도 근사를 제공하는 포괄적인 구성 요소이다.[11]
영구 저장소나 데이터베이스를 변경하는 통합 테스트는 테스트 실패 시에도 파일이나 데이터베이스의 초기 및 최종 상태를 고려하여 신중하게 설계해야 한다. 이를 위해 다음 기술들을 조합하여 사용한다.
- `TearDown` 메서드 (많은 테스트 프레임워크에 필수)
- `try...catch...finally` 예외 처리 구조
- 데이터베이스 트랜잭션 (원자성 보장 시)
- 테스트 전 데이터베이스 "스냅샷" 생성 및 각 테스트 후 롤백 (Ant, NAnt, 지속적 통합 시스템 등으로 자동화 가능)
- 테스트 "전" 데이터베이스 초기화 (테스트 실패 진단이 어려워질 수 있음)
적절한 테스트 범위 선정TDD에서 적절한 테스트 범위를 선정하는 것은 매우 중요하다. 단순히 모든 코드에 대해 100% 테스트 커버리지를 달성하는 것보다, 시스템의 중요한 기능과 비즈니스 로직에 집중하여 테스트하는 것이 효율적이다.
TDD는 클래스나 관련 함수 그룹(모듈)과 같은 작은 유닛을 테스트하는 데 중점을 둔다. 유닛을 작게 유지하면 다음과 같은 이점이 있다.
고급 TDD 방법론으로는 인수 테스트 주도 개발(ATDD)과 예시 기반 명세가 있다. 고객이 지정한 기준을 인수 테스트로 자동화하고, 이를 통해 개발팀은 사용자 스토리에 대해 고객이 원하는 바에 집중할 수 있다.[18]
하지만 TDD는 단위 테스트에 중점을 두기 때문에, 사용자 인터페이스, 데이터베이스, 컴퓨터 네트워크 구성과 같이 전체 기능 테스트가 필요한 부분에서는 충분한 테스트를 보장하지 못할 수 있다.[38] 따라서 단위 테스트뿐만 아니라 통합 테스트, 인수 테스트 등 다양한 수준의 테스트를 적절히 조합해야 한다.
TDD를 적용할 때 주의해야 할 점은 다음과 같다.
- 테스트 케이스 간의 종속성을 피해야 한다. 테스트 스위트가 깨지기 쉽고 복잡해질 수 있다.
- 상호 의존적인 테스트는 연쇄적인 가짜 음성을 유발할 수 있어 결함 분석 및 디버그 노력을 증가시킨다.
- 필요 이상으로 검사하는 "모든 것을 아는 오라클" 구축은 유지보수 비용을 증가시킬 수 있다.[19]
- 구현 세부 사항을 테스트하는 것은 지양해야 한다.
- 느리게 실행되는 테스트는 효율성을 저하시킨다.
복잡한 시스템에서는 효과적인 모듈식 설계가 TDD에 필수적이다. 높은 응집도와 낮은 결합도를 가진 구성 요소는 테스트를 용이하게 한다.
테스트 코드는 프로덕션 코드와 동일한 엄격함으로 관리해야 한다. 테스트 소프트웨어의 소프트웨어 아키텍처는 핵심 제품 아키텍처만큼 중요하다.
테스트 주도 개발 환경에서 생성된 단위 테스트는 일반적으로 테스트 중인 코드를 작성하는 개발자가 생성하므로, 코드와 동일한 사각지대를 가질 수 있다는 점을 인지해야 한다.
7. 1. 테스트 가능하도록 설계
객체 지향 설계에서는 비공개 데이터와 메서드에 직접 접근할 수 없다. 따라서 단위 테스트를 위해 추가 작업이 필요할 수 있다. 자바 등에서는 리플렉션을 사용하여 비공개 멤버에 접근할 수 있다.[13] 또는 내부 클래스나 .NET의 부분 클래스를 사용하여 테스트에서 비공개 멤버에 접근 가능하게 할 수도 있다.C/C++에서는 `#if DEBUG ... #endif` 같은 컴파일러 지시어를 사용하여 테스트 관련 코드가 릴리스 버전에 포함되지 않도록 할 수 있다.[13] 그러나 이는 릴리스 코드와 단위 테스트 코드가 정확히 동일하지 않음을 의미한다. 따라서 최종 릴리스 빌드에서 더 적지만 포괄적인 종단 간 통합 테스트를 정기적으로 실행하여, 테스트 도구에 의존하는 코드가 없도록 해야 한다.
비공개 메서드나 데이터 테스트의 필요성에 대해서는 논쟁이 있다. 일부는 비공개 멤버가 구현 세부 사항이므로 변경될 수 있어야 한다고 주장하며, 공용/보호된 인터페이스 테스트로 충분하다고 한다.[14] 반면, 핵심 기능이 비공개 메서드로 구현될 수 있으며, 직접 테스트가 더 작고 직접적인 단위 테스트의 이점을 제공한다는 주장도 있다.[15][16]
개발 중인 코드가 데이터베이스, 웹 서비스 등 외부 프로세스나 서비스에 의존하는 경우, 유닛 테스트 가능한 분리가 필요하다.[17] 이를 위해 다음 두 단계를 따른다.
# 외부 액세스가 필요한 경우, 의존성 역전 원칙에 따라 인터페이스를 정의한다.
# 인터페이스를 두 가지 방식(실제 외부 프로세스 접근, 가짜/모의 객체)으로 구현한다. 가짜 객체는 "Person 객체 저장됨" 같은 메시지를 추적 로그에 추가하여 동작을 확인한다.[17] 모의 객체는 자체 테스트 단언을 포함하여 더 정교한 검증을 할 수도 있다.
가짜나 모의 객체 메서드는 항상 동일하고 현실적인 데이터를 반환하여 테스트를 지원한다. 오류 처리 루틴 테스트를 위해 미리 정의된 오류 모드(유효하지 않은 응답, 예외 등)를 설정할 수도 있다. 가짜 암호화 서비스, 가짜 난수 서비스 등도 활용 가능하다. 이러한 방식은 의존성 주입의 예시이다.
테스트 더블은 UUT(Unit Under Test, 테스트 대상 유닛)가 의존하는 시스템 기능을 대체하는 테스트 특정 기능이다. 링크 시점 또는 실행 시점에 시스템에 도입될 수 있다. 링크 시점 대체는 테스트 더블을 로드 모듈로 컴파일하는 방식이다. 런타임 대체는 함수 포인터나 객체 대체를 통해 테스트 케이스 실행 중 실제 기능을 대체하는 방식이다.
테스트 더블의 유형 및 복잡성은 다음과 같다.
- 더미: 기본 반환 값을 제공하여 링크 시점 대체를 용이하게 한다.
- 스텁: 더미에 단순 로직을 추가하여 다른 출력을 제공한다.
- 스파이: 매개변수 및 상태 정보를 캡처하여 더 발전된 상태 유효성 검사를 허용한다.
- 모의: 테스트별 동작을 확인하기 위해 개별 테스트 케이스에 의해 지정된다.
- 시뮬레이터: 대상 기능에 대한 고충실도 근사를 제공하는 포괄적인 구성 요소이다.[11]
영구 저장소나 데이터베이스를 변경하는 통합 테스트는 테스트 실패 시에도 파일이나 데이터베이스의 초기 및 최종 상태를 고려하여 신중하게 설계해야 한다. 이를 위해 다음 기술들을 조합하여 사용한다.
- `TearDown` 메서드 (많은 테스트 프레임워크에 필수)
- `try...catch...finally` 예외 처리 구조
- 데이터베이스 트랜잭션 (원자성 보장 시)
- 테스트 전 데이터베이스 "스냅샷" 생성 및 각 테스트 후 롤백 (Ant, NAnt, 지속적 통합 시스템 등으로 자동화 가능)
- 테스트 "전" 데이터베이스 초기화 (테스트 실패 진단이 어려워질 수 있음)
7. 2. 적절한 테스트 범위 선정
테스트 주도 개발(TDD)에서 적절한 테스트 범위를 선정하는 것은 매우 중요하다. 단순히 모든 코드에 대해 100% 테스트 커버리지를 달성하는 것보다, 시스템의 중요한 기능과 비즈니스 로직에 집중하여 테스트하는 것이 효율적이다.TDD는 클래스나 관련 함수 그룹(모듈)과 같은 작은 유닛을 테스트하는 데 중점을 둔다. 유닛을 작게 유지하면 다음과 같은 이점이 있다.
고급 TDD 방법론으로는 인수 테스트 주도 개발(ATDD)과 예시 기반 명세가 있다. 고객이 지정한 기준을 인수 테스트로 자동화하고, 이를 통해 개발팀은 사용자 스토리에 대해 고객이 원하는 바에 집중할 수 있다.[18]
하지만 TDD는 단위 테스트에 중점을 두기 때문에, 사용자 인터페이스, 데이터베이스, 컴퓨터 네트워크 구성과 같이 전체 기능 테스트가 필요한 부분에서는 충분한 테스트를 보장하지 못할 수 있다.[38] 따라서 단위 테스트뿐만 아니라 통합 테스트, 인수 테스트 등 다양한 수준의 테스트를 적절히 조합해야 한다.
TDD를 적용할 때 주의해야 할 점은 다음과 같다.
- 테스트 케이스 간의 종속성을 피해야 한다. 테스트 스위트가 깨지기 쉽고 복잡해질 수 있다.
- 상호 의존적인 테스트는 연쇄적인 가짜 음성을 유발할 수 있어 결함 분석 및 디버그 노력을 증가시킨다.
- 필요 이상으로 검사하는 "모든 것을 아는 오라클" 구축은 유지보수 비용을 증가시킬 수 있다.[19]
- 구현 세부 사항을 테스트하는 것은 지양해야 한다.
- 느리게 실행되는 테스트는 효율성을 저하시킨다.
복잡한 시스템에서는 효과적인 모듈식 설계가 TDD에 필수적이다. 높은 응집도와 낮은 결합도를 가진 구성 요소는 테스트를 용이하게 한다.
테스트 코드는 프로덕션 코드와 동일한 엄격함으로 관리해야 한다. 테스트 소프트웨어의 소프트웨어 아키텍처는 핵심 제품 아키텍처만큼 중요하다.
테스트 주도 개발 환경에서 생성된 단위 테스트는 일반적으로 테스트 중인 코드를 작성하는 개발자가 생성하므로, 코드와 동일한 사각지대를 가질 수 있다는 점을 인지해야 한다.
참조
[1]
논문
Testability-driven development: An improvement to the TDD efficiency
https://www.scienced[...]
2025-01-01
[2]
웹사이트
Extreme Programming
http://www.computerw[...]
Computerworld
2011-01-11
[3]
서적
Test-Driven Development in Microsoft .NET
Microsoft Press
2004
[4]
서적
Working Effectively with Legacy Code
Prentice Hall
2004
[5]
웹사이트
Why does Kent Beck refer to the "rediscovery" of test-driven development?
http://www.quora.com[...]
2014-12-01
[6]
웹사이트
Why does Kent Beck refer to the "rediscovery" of test-driven development?
http://www.quora.com[...]
2014-12-01
[7]
서적
Test-Driven Development by Example
Addison Wesley
2002-11-08
[8]
웹사이트
Canon TDD
https://tidyfirst.su[...]
2024-10-22
[9]
간행물
Directing the Agile Organisation: A Lean Approach to Business Management
IT Governance Publishing
2013
[10]
웹사이트
Full Stack Testing
https://www.thoughtw[...]
2022-09-07
[11]
웹사이트
Effective TDD for Complex Embedded Systems Whitepaper
http://www.pathfinde[...]
Pathfinder Solutions
[12]
웹사이트
Agile Test Driven Development
http://www.agilesher[...]
Agile Sherpa
2012-08-14
[13]
웹사이트
Subverting Java Access Protection for Unit Testing
http://www.onjava.co[...]
O'Reilly Media, Inc.
2009-08-12
[14]
웹사이트
PEP 8 -- Style Guide for Python Code
https://www.python.o[...]
Python Software Foundation
2012-05-06
[15]
웹사이트
Testing Private Methods/Member Variables - Should you or shouldn't you
http://blogs.msdn.co[...]
Microsoft Corporation
2009-08-12
[16]
웹사이트
How to Test Private and Protected methods in .NET
http://www.codeproje[...]
CodeProject
2009-08-12
[17]
서적
Refactoring - Improving the design of existing code
https://archive.org/[...]
Addison Wesley Longman, Inc.
1999
[18]
문서
Test Driven: TDD and Acceptance TDD for Java Developers
Manning Publications
2007
[19]
Youtube
Test-Driven Development (TDD) for Complex Systems Introduction
by Pathfinder Solutions
[20]
서적
Lean-Agile Acceptance Test-Driven Development: Better Software Through Collaboration
Addison Wesley Professional
2011
[21]
웹사이트
BDD
http://guide.agileal[...]
2015-04-28
[22]
웹사이트
Effective TDD for Complex, Embedded Systems Whitepaper
http://www.pathfinde[...]
Pathfinder Solutions
2012-11-27
[23]
문서
Advantages and Disadvantages of Test Driven Development - LASOFT
https://lasoft.org/b[...]
[24]
논문
Testability-driven development: An improvement to the TDD efficiency
https://www.scienced[...]
2025-01-01
[25]
웹사이트
On the Effectiveness of Test-first Approach to Programming
http://nparc.cisti-i[...]
Proceedings of the IEEE Transactions on Software Engineering, 31(1). January 2005. (NRC 47445)
2008-01-14
[26]
웹사이트
TDD Proven Effective! Or is it?
http://theruntime.co[...]
2008-02-21
[27]
웹사이트
Stepping Through the Looking Glass: Test-Driven Game Development (Part 1)
http://gamesfromwith[...]
Games from Within
2007-11-01
[28]
서적
Projekt Engineering Ingenieurmässige Softwareentwicklung in Projektgruppen
Fachbuchverl. Leipzig im Carl-Hanser-Verl.
2005
[29]
웹사이트
About the Return on Investment of Test-Driven Development
https://pdfs.semanti[...]
Universität Karlsruhe, Germany
2012-06-14
[30]
서적
Test-Driven Development - An Empirical Evaluation of Agile Practice
Springer
2010
[31]
문서
The impact of Test-First programming on branch coverage and mutation score indicator of unit tests: An experiment.
[32]
문서
On the Effects of Pair Programming on Thoroughness and Fault-Finding Effectiveness of Unit Tests
[33]
문서
Impact of pair programming on thoroughness and fault detection effectiveness of unit test suites.
[34]
논문
"Impact of test-driven development on productivity, code and tests: A controlled experiment"
2011
[35]
논문
"A dissection of the test-driven development process: does it really matter to test-first or to test-last?"
2017
[36]
논문
An industry experiment on the effects of test-driven development on external quality and productivity
2016
[37]
논문
Experimental evaluation of test-driven development with interns working on a real industrial project
https://ieeexplore.i[...]
2020
[38]
웹사이트
Problems with TDD
http://dalkescientif[...]
Dalkescientific.com
2009-12-29
[39]
웹사이트
Are Unit Tests Overused?
https://www.simple-t[...]
Simple-talk.com
2012-10-19
[40]
웹사이트
Testing
http://people.apache[...]
HP Laboratories
2006-11-06
[41]
웹사이트
Fragile Tests
http://xunitpatterns[...]
[42]
웹사이트
First International Test Driven Development (TDD) Conference
https://tddconferenc[...]
2021-07-20
[43]
간행물
First International TDD Conference - Saturday July 10, 2021
https://www.youtube.[...]
2021-07-10
[44]
문서
[45]
웹인용
What is TDD? Everything About Test Driven Development
https://www.simform.[...]
2019-04-15
본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.
문의하기 : help@durumis.com