목차 (눌러서 이동)
Test Driven Development 를 공부하기 위해 처음 적용한 프로젝트에서 너무 간단하고 당연한 것까지 Test-Drive 방식으로 개발하는 것을 보고 꼭 그래야 하는지, 그렇지 않다면 어떤 기준을 적용할지 궁금해 관련 포스팅을 찾아보기 시작했다. 살펴본 포스팅들을 아래에서 순서대로 정리해 보았다.
The Pragmatics of TDD
필자인 Robert C. Martin 은 이전 포스팅인 The Startup Trap 이 개발자들 사이에서 논란을 일으키자 그에 대한 후속 포스팅을 한다.
The Startup Trap 을 간단히 요약하자면 많은 Startup 들이 창업 초창기의 과도한 열정(?) 으로 인해 속도에만 치중한 채 Test 의 중요성을 잊게 되고 결국 점점 통제 불능이 되어가는 Software 와 함께 몰락하게 된다는 내용이다. (The only way to go fast, is to go well. - Robert C. Martin)
위 포스팅이 약간의 논란이 되고, 필자는 TDD 의 원칙을 어느정도 따르되 실용적으로 적용하는 자신의 방법을 소개한다.
방법의 소개에 앞 서 필자는 TDD 를 프로그래머에게 있어 의사의 살균 처리된 수술 도구와 같다고 말한다. 과거에 그는 팔에 있는 혹을 제거할 일이 있었는데, 당시 의사가 살균된 도구가 아니라 "깨끗한" 수준의 도구를 사용했고 이에 항의하자 "이정도 수술에는 '깨끗한 도구' 로도 충분하다." 라고 말했다고 한다.
수술은 잘 마쳤지만 얼마 후 봉합 부위에서 감염이 일어나 다시 소독하는 과정을 거쳤고 그는 이것이 프로그래머가 TDD 원칙을 고수해야 하는 이유와 같다고 생각했다.
아래는 그가 실제로 Test 주도 개발에 있어 상황에 따라 Test 하지 않는 것이다.
- getters / setters. 해당 Properties 를 사용하는 Methods 에 대한 Test
로 인해 간접적으로 검사된다.
- 멤버 변수, 한 줄 짜리 함수, 명백히 "사소한" 함수들. 이러한 요소들은 모두
다른 Unit Tests 과정에서 간접적으로 검사된다.
- GUI. 단, 중요한 Logics 은 모두 검사 가능한 Module 로 옮겨 GUI 에는
단순히 Data 를 View 로 끌어오기 위해 필요한 것들만 있도록 정리한다.
- Frameworks, Databases, Web-Servers, Third-party Software 들은
보통 잘 Test 하지 않는다. (물론 잘 동작하지 않으면 Test 해야 한다.)
Test Trivial Code
The Pragmatics of TDD 에 대한 Mark Seemann 의 포스팅으로, Robert C. Martin 의 의견에 대해 반대하고 있다. 이에 대해 이후에는 자신이 잘 못 생각했다는 의견을 다시 포스팅하지만, 의미있는 내용도 있는 것 같다.
먼저, Test-Driven Development 라는 의미 자체는 Test 가 Development 를 Drive 한다는 것으로 Test → Implementation 은 원인과 결과 (Cause and Effect) 의 관계가 된다. 이런 전제에 있어 사소한 Code 라고 Test 하지 않을 Code 는 존재하지 않는다.
Robert C. Martin 은 getters / setters 를 Test 하지 않는다고 했는데, 사소하기 때문에 Test 하지 않을 거라면 애초에 그냥 Public field 로 노출하지 않을 이유도 없지 않은가? 하지만 이들은 캡슐화와 관련있는 부분이다. Data 들이 Public field 가 아닌 getters / setters 를 통해 노출되어야 하는 이유는, 이들이 Software 의 구현 방식을 추후에 변경 가능한 것으로 만들기 때문이다.
동작 결과는 같지만 구현 방식이 바뀌는 것을 Refactoring 이라 하는데, Refactoring 후에도 동일하게 동작함을 보장하기 위해 적절한 Test Suite 가 필요한 법이다.
성공적인 Code 는 오래 살아남고 계속 진화한다. 이런 Code 에서 지금 당장 사소하게 여겨지는 코드가 계속 사소한 것으로 남을지 알 수 없다. 중요한 것은 사소한 동작들까지 올바른 동작으로 남도록 하는 것이다.
Robert C. Martin 은 다른 Methods 의 Tests 로 인해 간접적으로 검사 된다고 하지만 다른 Methods, Tests 역시 계속 남을지 확신할 수 없다.
마지막으로, TDD 를 Pragmatic 하게 적용하는 것은 TDD 를 처음 배우는 사람들에게 혼란을 준다. Test 하지 않아도 될만큼 사소하다는 개념은 측정하기 어렵기 때문이다.
사소한 것들까지 Test 하는 것은 Cost / Benefit 비율의 측면에서 불리해 보이지만, Cost 를 줄여 나가는 방식으로 조금은 개선할 수 있다. (관련해 필자는 한 줄로 getter / setter 를 Test 하는 방법을 소개한다.)
On Testing "Trivial Code"
사소한 getters / setters 까지 모두 Unit Test 해야 한다는 Mark Seeman 의 포스팅에 대한 LosTechies 블로그의 반대 의견 포스팅이다.
Mark 가 사소한 Code 까지 모두 Test 돼야 한다고 말한 것에는 동의하지만 모두 "Unit Test" 해야 한다고 말한 것에 동의하지 않는다.
Mark 가 예시로 들었던 date/time auto-property 에 대해 얘기해보자면, 해당 property 가 제공하는 값은 Business logic 에 연관된 어떤 값이 아니라 단순한 계산서 날짜 혹은 계산서 상의 구매 순서 등일 수 있다. 이런 값들은 프로그램에 어떤 값을 제공하지만 사소한 Field 이다.
만약 이런 Field 가 단순히 화면에 보여지는 용도로만 사용된다면 Unit Test 하지 않을 것이다. 대신, 해당 Field 가 값을 제공하는 대상에서의 Test 를 더 신경 쓸 것이다.
즉, Unit Test 가 아니라 Functional Test 를 한다는 의미이다. 무슨 의미인가 하면 예를 들어 단순히 계산서 상의 정보를 표현하기 위한 property 라면 property 자체에 대한 Unit Test 를 하는게 아니라 계산서를 발행하는 사람과 이를 받는 사람이 확인하게 될 정보의 표현을 Test 하는 것이다.
Functional Test 를 작성해 계산서의 발행 과정을 검사하고 간단한 UI Test 를 통해 전달받은 계산서 정보를 확인하는 식의 End to End Test 를 적용한다.
결국, 사소한 Code 들 역시 그들이 Software 에 값을 제공하는 방식에 따라 Test 되어야 하지만 단순히 그들을 Unit Test 하는 방식으로 검사할 필요는 없다는 것이다.
무엇을 Test 하는가에 대한 아주 명징한 기준은 없다. 하지만, 아무리 사소한 Code 라도 그것이 Software 의 어떤 부분에 어떤 방식으로 가치를 제공하는지 고민해본다면 Test 의 방향을 잡을 수 있을 것이다. (만약 그 사소한 Code 의 가치를 찾을 수 없다면... 필요 없는 Code 일지도!)
What to test and not to test
다시 Mark Seemann 의 포스팅. 사소한 Code 까지 Unit Test 해야 한다고 했던 과거 포스팅의 오류를 인정하는 내용이다.
Test Trivial Code 를 포스팅 하던 당시 필자는 사용자들의 배포 상태에 대한 통제가 부족한 이른바 "Wildlife" open source library 를 개발했었다. 이런 Software 를 개발할 때는 문제가 생겼을 때 이전의 상태로 안전하게 돌아갈 수 있도록 해야 한다.
따라서 필자는 Backward Compatibility 를 가장 우선 순위에 두었고, Regression Test 를 위해 모든 API 를 일일이 Unit Test 했다. 필자와 같은 상황 혹은 의료용 Software 와 같이 한 번의 Error 가 치명적인 개발에는 100% 의 Test Coverage 를 위해 노력하는 것이 바람직할 것이다.
그런데 Software 개발과 Test 에는 여러 목적이 있다. TDD 의 관점에서 Test 를 작성하는 것은 Production Code 의 Design 에 대한 Feedback 을 받는 것이다. 말하자면, Unit Test 는 개발중인 제품의 첫 번째 고객이라 할 수 있는 것이다.
이러한 TDD 에서 Test 의 목적을 이해한 상태에서 다시 얘기해보자. 모든 것을 반드시 Test-Drive 해야 할까?
물론 경험이 부족한 사람이라면 완전한 Test-Driven 방식이 도움이 될 것 이고, 모든 것을 Unit Test 한다면 그 자체로 Regression Test 의 역할도 할 수 있으므로 장점도 존재한다.
하지만 Product 의 Design 에 대해 Feedback 을 받을 수 있는 정도의 Functional Test 를 작성하고, 이를 통해 직,간접적으로 모든 것을 검사할 수 있다면 모든 Field 에 대한 명징한 Unit Test 를 작성할 필요까진 없다.
필자는 과거 포스팅을 작성할 당시 TDD 와 Regression Test 를 혼동했다. 모든 Field 에 대해 Unit Test 했던 이유는 완벽한 Backward Compatibility 를 위한 것이었지 TDD 를 위한 것이 아니었다.
Robert C. Martin 이 말한 TDD 의 Pragmatics 에 대해 좀 더 말하자면, 무엇이 되었든 처음에는 규칙을 준수해야하고 숙달이 됨에 따라 자신만의 방법을 찾게 되는 것이다. 필자 역시 실제로 TDD 의 방식으로 개발할 때 실용적인 방법을 사용하고, 주변의 많은 숙련자들이 그렇게 한다고 한다.
하지만, 실용적인 방법을 채택할 수 있는 것은 경험해본 자의 특권이다. "You can only be pragmatic if you know how to be dogmatic."
'개발 관련 이것저것 > 개발 방법론' 카테고리의 다른 글
TDD(Test Driven Development) 와 BDD(Behavior Driven Development) (0) | 2021.07.17 |
---|