목차
https://product.kyobobook.co.kr/detail/S000001223650
테스트 주도 개발 TDD 실천법과 도구 | 채수원 - 교보문고
테스트 주도 개발 TDD 실천법과 도구 | 효율적인 설계와 간결한 코드를 만드는 필수 TDD 기법『TDD 실천법과 도구』는 업고픔질 소프트웨어를 만드는 유쾌한 개발 비법 TDD를 다룬 책이다. 초급 개
product.kyobobook.co.kr
전통적인 개발 및 테스트의 문제점들
- 개발 기간이 길어질수록 개발자의 목표 의식이 흐려진다.
- 어디까지 짰더라?, 내가 뭘하고 있는거였지?, 이 모듈이 무슨 기능을 해야하더라?
- 분량이 늘어날수록 확인이 어려워진다.
- 개발자의 집중력이 필요해진다
- 코드의 사용방법과 변경 이력을 개발자의 기억력에 의존하게 돼서 논리적 오류를 찾기 어려워진다.
- 여기서 이값이 들어가고 이값이 나가는게 맞나??
- 테스트를 점차 간소화 하는 항목들이 늘어난다.
- 코드 수정 시 기존 코드의 정상 동작에 대한 보장이 어렵다.
- 아 여기 고쳤는데 이거 다른 데서도 썼던가..?
- 테스트를 해보려면 기존 코드를 수정해야하는 등 번거로운 선행 작업이 필요할수있다.
- 입고 처리 테스트 하려면 우선 주문 완료됬다고 테이블을 업데이트 시켜야하고…등등
- 테스트는 개발자의 노동력을 소모한다.
TDD란 ?
- 메소드나 함수 같은 모듈을 작성할 떄 ‘ 작성 종료 조건을 먼저 정해놓고코딩을 시작’
- 빠른 피드백을 통해 만들어야할 프로그램’을 학습해나가는 점진적인 기법. 피드백과 학습
- 테스트 케이스를 작성하는 가장 기본적인 과정인 함수를 생성하기 전에 그 함수가 무엇 을 수행하고 무엇을 처리해야 하는지에 대해 생각해보는 것은 여행을 떠나기 전에 지도를 한번 살펴보는 것과 크게 다르지 않다.
- 내가 도착해야 할 곳이 어디인지를 마음속 에 선언하는 것은 내가 다른 유혹에 빠지지 않도록 해주고 본래의 의도 또한 좀 더 명확하 게 하는 데 큰 도움을 줄 수가 있다.
- 또한 이런 믿음과 확신을 바탕으로 테스트 케이스 를 동료와 공유함으로써 개발자의 인식 상태를 '나'의 코드가 얼마나 컴퓨터에서 잘 동작할 까 하고 고민하는 맹목적 효율성'으로부터 '우리'의 코드가 얼마나 쉽게 기억해낼 수 있고, 잘 이해할 수 있는지와 같은 '인간성'에 대한 실천으로 옮겨놓을 수 있다.
- 간혹 개발자 로서 아키텍처 설계와 같은 구조적 혹은 기술적으로 아름다운 상위 레벨의 어떤 것들을 동경하게 된다.
- 하지만 작은 피드백에 좀 더 신경을 쓰고 자신의 표현을 좀 더 인간의 어떤 것에 가깝게 하는 것은 작고 초라해 보일 수 있을지 몰라도 우리를 가장 안전하고 행복하 게 하는 책임감' 있는 움직임이라 이다.
- humans for humans(프로그래밍은 '사람'을 위해 '사람'이 행하는 인간적인 작업이다)
- TDD가 가지는 무엇인가에 '반응'하고 그것에 책임'을 수 정신과 관련된 내용.
- TDD에서 가장 중요한 사항은 무엇인가를 해결하는 데만 집중하는 것이 아니라, 하고자 하는 게 무엇인지에 정신이 머무르게 하는 것입니다.
- 테스트를 통과 시키는 것은 나에게 주어진 '책임'에 정중히 '응답'하는 것.
진행방식
- 질문 - 테스트작성을 통해 시스템에 질문한다.(결과는 실패)
- 작성하고자 하는 메소드나 기능이 무엇인지 선별하고 작성 완료 조건을 정해서 실패하는 테스트케이스 작성
- 응답- 테스트를 통과하는 코드를 작성해서 질문에 대답( 결과는 성공)
- 리팩토링- 아이디어를 통합하고, 불필요한건 삭제하고, 모호한건 명환하게 리팩토링
- 가독성이 적절한다
- 중복된 코드는 없는가
- 이름이나 잘못 부여된 메소드나 변수명은 없는가
- 구조의 개선이 필요한 부분은 없는가?
- 반복- 다음 질문을 통해 계속 진행
접근방식
- TDD에서는 테스트자동화를 통해서 개발이 시작된 시점부터 완료될 때 까지 가능한 빠른 시점내에, 자주 실패를경험하도록 유도한다.
- OK 조건을 사전에 정해두고 빠르게 실패를 경험하며, 그 조건을 등대로 삼아 실패 상황을 최대한 빨리 극복하고자 노력한다.
- 성공한 항목과 실패한 항목이 명확하고, 작업해야하는 부분이 확실하다.
- 설공에 필요한 조건을 만들고, 실패하는 조건항목을 성공시킨다.
- TDD를 하며 테스트케이스 자체가 “Amount 클래스의 구조에 대해 생각해봐라” 라는 말을 건다
- 충분한 만큼의 테스트 케이스가 작성됐다 생각하면 제대로 구현시작
- 모호하지만, 현재 집중하고 있는 요구 사항그 한가지만을 통과하기에는 적절한 숫자의 테스트케이스.
- 경험이 축적됨에 따라 노련해지는 부분
TDD의 장점
- 개발의 방향을 잃지 않게 유지해준다
- 현재 자신이 어떤 기능을 개발하고 있고, 또 어디까지 와 있는지를 항상 살펴볼 수 있다.
- 그리고 남은 단계와 목표를 잊지 않게 도와준다.
- TDD를 진행할 때 만들어지는 테스트 케이스들, 자신이 어디까지 왔고, 앞으로 나아가야 하는 곳이 어디인지를 알려주는 나침반이 된다.
- 품질 높은 소프트웨어 모듈 보유
- TDD를 통해 만들어진 애플리케이션은 필요한 만큼 테스트를 거친 품질이 검증된 부 품'을 갖게 되는 것과 마찬가지다.
- 품질 좋은 부품이 꼭 품질 좋은 제품을 보장해주는 건 아니지만, 좋은 제품을 만드는 데 있어 기본 조건임에는 틀림없다.
- 자동화된 단위 테스트 케이스를 갖게 된다
- TDD의 부산물로 나오는 자동화된 단위 테스트 케이스들은, 개발자가 필요한 시점에 언제든지 수행해볼 수 있다. 그리고 그 즉시 현재가자 작성된 시스템에 대한 이상 유무 를 바로 확인할 수 있다.
- 또한 기능을 추가한다든가, 수정하게 됐을 때 수행해야 하는 회귀 테스트에 대한 부담도 줄이는다.
- 사용설명서 & 의사소통의 수단
- TDD로 작성된 각 모듈에는 테스트 케이스라고 하는 테스트 코드가 개발 종료와 함께 남게 된다
- 테스트 코드들의 가치는 시간이 지나면서 두고두고 빛을 발한다.
- 그 가치 안에는 고객만을 위한 것이 아 니라, 현재의 자신과 주위의 개발자, 그리고 미래의 개발자에게 제공되는 상세화된 모 듈 사용 설명서라는 부분도 포함되어 있다.
- 즐겨 보는 물건은 아니지만, 그렇다고 매뉴 얼 없는 TV 등의 가전 제품을 받는다면, 제품에 대한 느낌이 어떨까? TDD를 통해 작 성된 테스트 케이스는 사용 설명서이자, 그와 동시에 다른 개발자와 소통하는 커뮤니 케이션 통로가 된다.
- 사실, 무한한 상상력을 필요로 하는 기존 코드(egacy codes)의 난해함과 문서화 부재 가 주는 상실감이 IT 프로젝트에서 얼마나 악영향을 미치는지에 대한 연구 사례는 많다.
- TDD는 이 부분에서도 장점을 갖는다. 일반적으로 TDD는 리팩토링과 단짝으로 진행되며, 리팩토링의 최대 미덕은 사람이 이해할 수 있는 코드로 만든다'이다.
- 또한 TDD의 산물인 테스트 케이스들은 그 자체가 API들의 사용 예가 돼준다. 대부분의 개발자는 API 참조매뉴얼을 좋아하지 않는다. 그보다는 상황에 들어맞도록 잘 만들어진 예제를 더 좋아한다.
- 테스트 케이스를 잘 작성하면, 개발 문서뿐 아니라 테스트 문서까 지도 함께 해결할 수 있다.
- TDD로 작성된 각 모듈에는 테스트 케이스라고 하는 테스트 코드가 개발 종료와 함께 남게 된다
- 설계 개선
- 테스트 케이스 작성 시에는 클래스나 인터페이스, 접근제어자(access modifier), 이름 짓기(naming), 인자(argument) 등에 이르는 개발에 포함된 다양한 설계 요소 들에 대해 미리부터 고민하게 된다.
- TDD에서 개발자는 자신이 작성한 프로그램에 대해 메소드 혹은 함수 단위로 테스트를 수행하며, 이는결과적으로 이후에 **발생한느 테스트 단계(통합, 인수..)에서의 결함 발생 비용을 줄여**준다.
- 결함을 빨리 발견하면 할수록 적은 비용으로 처리가 가능하다.
- 흔히 테스트하기 어렵다고 생각되는 코드들은 객체 설계 원리 중 기본에 해당하는 원칙들이 잘못 적용됐거나 충분히 고려되지 않 았을 가능성이 높다.
- TDD를 진행해나가면서, 테스트가 가능하도록 설계 구조를 고 민하다 보면 자연스럽게 디자인을 개선하게 된다.
- 즉 누가 모니터 옆에서 손가락으 로 개선해야 하는 부분을 가리켜주는 것이 아니라, 스스로의 사고와 수련으로 발전 해나갈 수 있게 도와준다.
- 테스트 케이스를 작성할 때는, 작성하게 되는 모듈이 접할 상황에 대해 사전에 고 민해보고 준비하게 된다.
- 미리 고민해보고 작성하는 것과 작성하면서 그때그때 추가하 는 것은, 모듈의 응집도와 의존성 정도에 있어 적지 않은 차이를 만들어낸다.
- 테스트 케이스 작성 시에는 클래스나 인터페이스, 접근제어자(access modifier), 이름 짓기(naming), 인자(argument) 등에 이르는 개발에 포함된 다양한 설계 요소 들에 대해 미리부터 고민하게 된다.
- TDD가 주는 설계상의 이점
- TDD 자체가 단위 단위의 작은 설계를 만들어 낸다.
- 입력과 출력, 해당 모듈이 동작하기 위해 필요한 요소 파악 등이 사전에 확실하게 고려되고 테스트 된다.
- 이로 인해 다른 모듈에 대한 의존성이 상대적으로 적다.
- 왜냐하면 다른 모듈에 의존성이 많아지면 적절한 테스트케이스를 작성하기가 점점 어려워 지기 때문에, 개발자가 스스로 모듈의 의존성을 줄이려는 노력을 초반부터 해나가게 된다.
- 또한 각 모듈은 최대한 테스트를 독립적으로 수행할수 있도록 만들기 때문에 자기 오나결성이 높아진다.
- 결과적으로 신뢰수준을 따져봐야 하는 모듈에 대해, 개발자가 접근해야 하는 영역을 좀 더 추상화된 상위 레벨로 올려줄수 있기 때문에, 소프트웨어의 복잡도가 낮아지고 개발자가 좀 더 일반화되고 추상된 형태의 디자인에 더 집중할 수 있게 도와 준다.
- TDD와 OOP
- TDD를 하면 기능과 객체의 관계를 스스로 먼저 고민하게 된다.
- 그리고 떄떄로 현재와 같은 클래스 구조로는 테스트 코드를 미리 작성할 수가 없다는 사실을 스스로 알아차리게된다.
- 테스트 케이스 코드는 입력과 출력이 명확하고, 사용되는 재료가 적으면 적을수록 작성하기 쉽다.
- 즉, 의존과계가 많은 코드는 테스트 코드 자체를 만들기가 어렵다.
- 사람의 본성이라는게 어려우면 안하게 된다.
- 의존관계를 최대한 만들지 않고, 기능이 동작하도록 노력을 하게된다.
- TDD는 자신의 모듈에 필요한게 무엇인지를 미리미리 고민하게 만든다. 기능 위주로 테스트를 하기 때문에 불필요한 속성/필드가 무엇인지도 초반부터 밝혀진다.
- 결과적으로 TDD는 좀 더 나은 설계, 좀 더 낭느 모듈 디자인이 될 수 있도록 스스로 생각할 수 있게 유도하고, 결과적으로는 모듈이 더 나은 구조를 가질 수 있게한다.
Mock
- 모듈의 겉모양이 실제 모듈과 비슷하게 보이도록 만든 가짜 객체를 MOck이라고 한다.
- 실제 객체를 만들기엔 비용과 시간이 너무 많이 들거나 의존성이 길게 걸쳐져있어 제대로 구현하기 어려울 경우, 이런 가짜 객체를 만들어 사용한다.
- 구현하는데 필요하지만 실제로 준비하기엔 여러가지 어려움이 따르는 대상을 필요한 부분만큼만 채워넣어서 만들어진 객체.
- 언제 Mock 객체를 만드나?
- 모듈이 필요로하는 의존성은 테스트작성을 어렵게 만든다. 그리고 그 의존성을 단절시키기 위해 사용된다
포비의 팀 내에 테스트 개발 문화 만들기
- 팀장의 역할
- 단위 테스트의 필요성을 인지하고 적극적으로 지원해줘야 한다.
- 프로젝트 초반에 개발 생산성이 저하될 가능성이 크기 때문에 효과가 나타날 때까지 인내하고 기다려줄 수 있어야 한다.
- 단위 테스트 및 소스코드 품질의 중요성을 강조하고, 잘하는 개발자에게 포상을 해줘야 한다.
- 팀원의 역할
- 단위 테스트의 필요성을 인지하고 단위 테스트를 만드는 데 적극적으로 참여해야 한다. 필요 없다고 판단된다면 적극적으로 의견 개진한다.
- 모든 개발자가 단위 테스트를 작성해야 한다. 일부 개발자만 참여할 경우 효과가 희석되거나 실패할 가능성이 많다.
- 좋은 단위 테스트 코드를 만들기 위해 끊임없이 공부하고 노력할 자세가 되어 있어야 한다.
- 팀의 역할
- 단위 테스트의 필요성을 인지하고 팀의 문화로 만들기 위한 지속적인 노력을 한다.
- 팀 구성원끼리 지속적으로 정보를 공유하고 배우려는 문화를 정착시켜야 한다.
- 하지 말아야 할 일
- 단위 테스트를 내가 원해서가 아니라 위에서 시키니까 한다는 수동적인 자세로 시작할 계획이 라면 하지 않는 편이 낫다. 오히려 시간 낭비가 될 가능성이 많다.
- 항상 100%가 실행되지 않는 테스트 코드는 추후 쓰레기가 될 가능성이 높다. 쓰레기가 된 테 스트 코드를 유지보수하는 비용은 낭비되는 시간일 뿐이다.
- 단위 테스트의 효과를 높일 수 있는 팁
- 지속적 통합 툴(C)을 도입하여 자동화된 단위 테스트가 가능하도록 한다.
- Test Coverage 리포트에서 복잡도가 높으나 단위 테스트 Coverage가 낮은 코드를 우선적으 로 단위 테스트한다.
- 가능하면 테스트 코드를 먼저 만드는 TDD를 습관화한다. 단, 많은 시간과 노력이 필요하다.
- 코드의 복잡도를 낮출 수 있도록 지속적으로 리팩토링한다.
- 테스트하기 쉬운 코드를 만들기 위해 노력한다.
유의사항
- 테스트케이스는 이름이 중요하다
- 이름이 길어지더라도 해당 테스트가 무엇이고, 실패한 세부 항목이 무엇인지 곧바로 알수 있어야한다.
- 더이상 제대로 동작하지 않는 테스트케이스는 제거한다.
- 자동화된 테스트는 소스의 품질을 좌우하는 중요한 자산이지만, 많다고 무조건 좋진않다.
- 중복된 케이스나 업무 변경등의 이유로 제대로 동작하지 않는 테스트 케이스는 제거한다.
- TDD는 자동화된 테스트를 만드는것이 목표가 아니다.
- TDD는 개발의 목표 지점을 미리 정하기 위해 단위 테스트 케이스를 만들고, 목표 상태 도달 여부를 빨리 확인하기 위해 단위 테스트 케이스를 자동화 시킨다.
- 자동화된 단위 테스트들은 TDD의 부산물이다.
- 개발과 설계를 위한 보조 도구이지, 목적은 아니다.
- 하나의 테스트케이스는 하나만 테스트 하도록 작성한다.
- 여러개의 실패하는 테스트케이스를 한번에 만들지 않는다.
- 테스트케이스는 최대한 고립시킨다.
- 각각의 단위 테스트는 다른 모듈이나 시스템에 최대한 독립적이고 고립된 형태로 작성될수록 단단한 테스트가 될 수 있다.
- 그래서 가급적이면 아래와 같은건 들어가지 않도록 작성해야한다.
- 테스트케이스가 작성되어있지 않은 모듈
- DB,외부 시스템, 콘솔출력, 네트웤,
- 물론 위 나열한것 자체를 테스트한다면 달라지겠지만, 대부분의 경우 현재 기능을 위해 외부에 의존하는 것들이다.
- 결국 최대한 의존관계를 줄이고, 경우에 따라Mock객체를 이용해서라도 독립성을 보장해줘야 특정 테스트 실패에 따른 실패 전파현상을 최소한으로 줄일 수 있다.
- 생성자테스트
- 객체 사용을 위해 반드시 갖춰야 하는 값을 생성자에 설정하는 경우 필요
- 선행조건이나 업무 로직이 섞여있는경우
- 테스트코드에서는 중복에 대한 접근이 약간다르다.
- 중복 되는 부분이 테스트 시나리오의 일부라면 곰니해봐야한다.
- 테스트 메소드가 하나의 문맥 측면에서 완결성을 갖는것이 중요할떄도 있음.
- 텍스트 픽스처 메서드는 그럼 언제?
- 테스트 시나리오에 참가하고 있는 객체들에게 테스트에 필요한 사전 조건이나 기반 환경을 제공할때.
- 유연한 코드
- 변경이 쉬워지려면 코드가 유연해야하는데, 유연함이란 ‘개발비용증가’의 또 다른 요소가 된다.
- ‘현재 필요한 기능에만 최대한 집줍’하는게 적절한 선택이다.
- 자동화된 테스트케이스는 소스가 작성된 후 작성하려고 하면, 심리적인 요인에 다가 테스트 작성에 소요되는 시간과 육체적 상황으로 인해 쉽지 않다.
- 아 귀찮아 이미 잘 돌고 있다구 !
- 어우 피곤해 그만하고 쉬어야지
- TDD를 할때 하나의 단계가 끝날 때 마다 리팩토링을 하면, 대상 코드 뿐 아니라 자동화된 테스트케이스 자체도 사용하기 좋은 코드로 정련된다.
- 리팩토링은 복잡하거나 어려운 그 무엇, 이를때면 디자인 패턴 같은 부류가 절대 아니다.
- 쉽게 배우고 크게 효과를 볼 수 있어야한다.
- 궁극적인 목표는 ‘이해하기 쉽고, 수정하기 쉬운 코드로 만들자’이다
- TDD는 설계를 위한 기법이고, 작업을 해나가려면 떄떄로 많은 사고와 전략이 필요한 기법이다.
- 보통사용자 스토리 보드를 완성하기 위한 세부 TOdo 리스트가 만들어진 다음에, 짝 프로그래밍으로 TDD를 적용해보면 효과가 더 높다.
- TDD로 개발하려면 단순히 자판은 누르면서 진행해나가는 시행착오 반복 식 보다 초반에 고민해야하는 요소가 더 많다.→ 큰 에너지 소비이자 고통,
- TDD를 어렵게 하는 요인
- TDD에 집중하는건 단위 기능(메소드)이지만 객체지향언어로 작성된 대부분은 클래스들로 구성된다.
- 클래스는 API나 라이버르리와 달리 권한, 위임, 책임 등을 갖고 외부와 통신한다.
- TDD라는 행위 자체, 혹은 TDD를 통해 만들어진 자동화 된 단위 테스트는 그 영역을 커버하지 못한다. 그래서 전체적으로 산만한 아키텍쳐가 되거나 한쪾으로 치우칠수있따.
- 이런 현상은 TDD만이 지나치게 강조됐을 떄 발생한다.
- 해결방법은 TDD를 진행하기 앞서 적절한 레벨의 설계를 선행하는것이다.
- 적어도 고수준 설계 정도는 마친다음 TDD를 적용해야 이런문제를 막을수있다.
- 우리는 종종 테스트 코드를 작성해야한다에 지나치게 집중해버린 나머지 TDD의 목표가 테스트 작성이 아닌 좀 더 나은 설계에 있다는 사실을 잊느다.
- TDD진행시, 억지로 테스트를 작성하는 건 잘못된 접근이다.
- 테스트를 작성하기 쉽게코드를 작성하는게 맞는거다.
- 테스트를 만들기 어렵다면, 기본적으로 코드 설계를 다시 봐야한다.
- 어떻게든 테스트케이스를 작성하기 위해 다양한 트릭(Mcok 프레임워크 대거 사용), 테스트 유틸리티 작성 → 결국 유지하기 어렵다.
- TDD는 버그를 없애기 위한 방법론이 아니다.
- 해당 프로그램의 특성, 도메인의 성격에 맞는, 그리고 작업하는 팀에 맞는 테스트 커버리지 비율을 찾아라.
- 자동화된 테스트 케이스는 TDD의 부산물이자, 리팩토링을 위한 안전장치에 더 가깝다.
- 그 자체를 순수 목적으로 삼게되면, 더이상 TDD의 영역이 아니게 된다.
- 그건 테스트의 영역이 된다.
'외부활동 > 우아한테크코스 [프리코스]' 카테고리의 다른 글
| [우아한테크코스 8기] 프리코스 오픈미션 3차 회고- 로또 TDD (0) | 2025.11.19 |
|---|---|
| 테스트 주도개발 TDD 실천법과 도구 정리 도서 요약[FAQ & 설계사고과정] (0) | 2025.11.13 |
| [우아한테크코스 8기] 프리코스 오픈미션 2차 회고- 자동차 경주 TDD (0) | 2025.11.13 |
| [우아한테크코스 8기] 프리코스 오픈미션 1차 회고- 문자열계산기 TDD (0) | 2025.11.09 |
| TDD 핵심 정리 (0) | 2025.11.08 |