처음 프로젝트를 시작하며 테스트를 작성하는 것 보다 기존의 작성된 코드(Legacy Code, 이하 레거시 코드)를 테스트하기 위한 테스트 코드를 만드는 것은 매우 어려운 일이다. 이 점은 많은 개발자가 실제 경험하면서도 이해하고 있을거라 생각한다. 레거시 코드를 테스트 하기 위해 만들어지는 테스트는 모든 비지니스 로직을 커버하기란 사실상 불가능하다. 테스트 코드는 신규 비지니스 로직보다 먼저 말들어 져야하고, 레거시 코드를 수정하는 경우에도 실패하는 테스트케이스(Failed Testcase)가 먼저 작성되어야 한다.
TDD : 실제 구현 코드 작성 전에 테스트부터 작성하자
테스트 주도 개발(Test Driven Development, 이하 TDD)은 애자일 개발 방법론 중 하나인 익스트림 프로그래밍(eXtream Programming)의 개발 방법 중 하나로 처음 소개되었다. TDD는 말 그대로 테스트가 개발을 주도하는 의미인데 비지니스 로직을 작성하기에 앞서 해당 로직을 테스트하는 테스트 코드를 먼저 작성하는 방법이다.
이 방법은 아키텍트가 완전한 설계 문서를 작성하고 해당 설계 문서만을 기반으로 하여 개발을 하던 개발 방식과는 매우 다른 방식으로 처음 접하는 개발자들은 적응이 쉽지 않은 방법이기도 하다. 설계 문서를 기반으로 한 개발방법은 예상한 결과물을 알고 있고 해당 결과물을 코드로 표현하는 방식이였다. TDD와는 조금 결이 다르다.
TDD는 유지보수성, 가독성, 비용, 안정성등을 고려하고 확보하는 것을 목표로 클린 코드를 작성하고자 한다.
TDD의 장점
TDD의 테스트케이스들이 개발의 나침반 역할을 해줌으로 개발의 방향을 잃지 않게 해준다. 간혹 개발을 진행하다 개발자의 고심 끝에 새로운 기능이 추가/삭제 되거나 로직이 변경되거나 하는 등의 일이 발생하는 경우를 경험하게 되는데, 그러한 상황을 방지해 준다.
TDD는 단위 테스트를 기반으로 하므로, 작은 단위의 설계를 만들어낸다. 테스트케이스를 작성하고 통과시키려면 설계가 더 명확해져야 한다. 작은 단위의 설계는 모듈의 동작을 보다 더 정확하게 설계되고 테스트하게 된다. 이러한 디자인으로 인해 다른 모듈에 대한 의존성이 적게 작성되고, SW의 복잡도가 낮아진다. 이 과정을 통해 자연스럽게 소프트웨어의 아키텍처가 좋은 구조로 변화한다.
이해하기 쉽고 모듈화된 테스트 코드와 SW 개발 비용 부담이 감소하며, 자동화된 단위 테스트 케이스를 가진다. 자동화된 단위 테스트 케이스는 현 시스템을 증명하기도 하지만, 기능을 추가하거나 수정하게 됐을 때 테스트 부담을 줄여준다.
이 외에도 많은 장점이 있다.
TDD의 단점
테스트 코드도 코드이기 때문에 해당 테스트코드를 유지보수하기 위한 비용이 발생하게 된다. 또한 테스트 코드도 코드 이기에 작성하는데 어려움이 존재하고 좋은 테스트 코드를 작성하는 것은 더 난이도가 높다.
추가로 비지니스 로직을 구현하기에도 시간이 부족한데 테스트 코드 또한 작성하게 되면 개발 속도가 저하된다고 생각하기 쉽다. 단기간 내 개발 속도 저하 (Slow down development in short time)가 발생하는 것은 사실이다. 마지막으로 기존의 레거시 코드에 적용하기에 많은 어려움이 있다.
TDD 테스트 작성의 간략한 정리
TDD에 대해서 깊게 설명하지는 않을 것이기에 TDD의 간단한 원칙만 정리하고 넘어가고자 한다. 테스트 코드를 작성하는 순서는 아래와 같다.
- Function Signature를 작성한다.
- 처음 작성시에는 항상 실패하는 테스트를 작성한다. (테스트 코드 작성시에는 Function 내부에 어떠한 로직도 없기에)
- Test가 Pass하는 로직을 Function 내에 구현한다.
너무 단순화하였지만 큰 그림에서 설명하면 위의 3단계를 거치며 테스트코드와 비지니스 로직이 구현된다.
TDD가 왜 좋은지는 Kent Beck의 글을 인용하여 마무리한다.
코드의 미래에 대해 고려하지 않음으로 인해, 코드가 더 뛰어난 적응성을 가질 수 있게 한다. 비록 발생하지 않은(혹은 아직 발생하지 않은) 변주 종류는 잘 표현할 지 못할지라도, 발생하는 변주 종류는 잘 표현하게 해준다. TDD는 시간이 지남에 따라 (개발자의) 코드에 대한 자신감을 점점 더 쌓아가게 해준다. 시스템 행위에 대한 자신감을 더 많이 얻게 된다. 프로젝트를 시작하고 시간이 지난 후에 더 좋은 느낌을 갖게 하는데 도움을 준다. 어떤 판단을 테스트에 담아 낼 수 있을 때, 설계논의는 훨씬 더 흥미로워진다. 우선 시스템이 이런 식으로 동작해야 하는지 저런 식으로 동작해야 하는지 논의할 수 있다. 일단 올바른 행위에 대해 결정을 내린 후에, 그 행위를 얻어낼 수 있는 최상의 방법에 대해 논의할 수 있다.
Kent Beck | TDD By Example
무엇이 좋은 테스트를 만드는가?
좋은 테스트를 만드는 것에는 3가지 특성이 존재한다. 스코프(Scope), 속도(Speed), Fidelity(정확도?) 이다.
Scope는 실제 코드의 얼마나 많은 부분을 테스트하고 커버할 수 있는지에 대한 특성을 의미한다. 개발자가 테스트를 작성할 때 가장 기본적인 실패와 더불어, 코너케이스, 예외처리 등 다양한 상황에 대한 동작을 테스트하고 커버할 수 있어야 한다.
Speed는 테스트가 얼마나 빠르게 수행되는지에 대한 특성이다. 유닛테스트에 경우 매우 자주, 빈번하게 실행이 될 것이다. 어떤 팀에서는 빌드 시 마다 모듈의 단위 테스트를 재 실행하는 경우도 존재할 것이다. (내가 속한 팀에서는 대부분 이와 같은 식으로 테스트를 진행했다.) 만약 테스트가 서버와 인터렉션이 필요하거나 다른 시스템과 동기화가 필요한 경우에는 테스트가 매우 오래 걸릴 수 있다. 이러한 테스트는 옳지 않다.
Fidelity는 정확도로 작성한 테스트가 실제로 얼마나 해당 비지니스 로직을 정확하게 테스트할 수 있는지에 대한 특성이다. 즉 실제 시나리오에 대해 얼마나 잘 테스트가 되는지에 대한 것이다. 실제 사용자 시나리오에는 절대 발생하지 않은 시나리오에 대해서도 테스트를 작성하는 경우는 불필요한 비용만 낭비하게 된다.
그래서.. 얼마나 많은 테스트를 작성해야 하는가?
앞서 설명한 바와 같이 테스트를 작성하는 것도 코드를 작성하는 것이기에 코드를 작성하는 비용이 수반된다. 그리고 테스트 코드 또한 유지보수가 필요한 코드이고 좋은 테스트를 작성하기 위한 시간과 투자는 항상 발생하게 된다.
그렇기에 누구나 궁금해 한다. 얼마나 많은 테스트 코드를 작성하면 되는가? 코드 커버리지가 80% 이상 커버하는 정도면 테스트가 적당한 것이 아닌가? 등등 팀마다 개인마다 기준이 다를 수 있다.
개인적으로 생각하는 테스트의 양은 필요한 만큼 작성해야 한다. 하지만 필요한 만큼 많이 작성해야 한다. 가 개인적인 의견이다. 좋은 테스트 코드는 많으면 많을수록 좋다. 물론 초기 개발 단계에 개발속도를 저하시키는 요인으로 평가할 수도 있으나 개발이 진척되면서 잠재적 에러가 초기에 발견되고 수정되는 과정을 거치면서 추후 개발 부채를 크게 줄일 수 있는 역할을 한다. 그렇기에 테스트는 필요한 만큼 작성해야 한다. 하지만 필요한 만큼 많이 작성해야 한다.
Reference
http://xunitpatterns.com/
https://www.stickyminds.com/article/shift-left-approach-software-testing
https://blog.qatestlab.com/2018/06/12/when-automate-testing/
https://en.wikipedia.org/wiki/Test-driven_development
http://gamesfromwithin.com/backwards-is-forward-making-better-games-with-testdriven-development
Test Driven Development By Example, Kent Beck
Clean Code, Robert C. Martin
TDD 실천법과도구, 채수원, 한빛미디어
Quality Code, Stephen Vance, Addison-Wesley
Test-Driven Development for Embedded C, James W. Grenning
'Android > Testing' 카테고리의 다른 글
[Android/Testing] #6. 테스트 시작 전에 테스트 더블(Test Double) 이해하기 (0) | 2022.10.08 |
---|---|
[Android/Testing] #5. 테스트를 위한 기반 프로젝트 생성하기 (0) | 2022.10.02 |
[Android/Testing] #4. Room Database 테스트 (0) | 2022.09.18 |
[Android/Testing] #3. Unit Testing 입문하기 (0) | 2022.09.15 |
[Android/Testing] #1. 왜 테스트를 해야 하는가? (0) | 2022.09.06 |