•
시스템을 변경하는 방법은 크게 두 가지로 나눌 수 있는데, 저자는 이를 각각, 편집 후 기도하기와 보호 후 수정하기라고 부른다.
•
편집 후 기도하기
◦
이는 코드 변경 계획을 신중하게 세우고, 변경 대상 코드를 이해했는지 확인한 후 변경 작업에 들어간다.
◦
변경 완료 후에는 시스템을 실행해서 변경 사항이 제대로 동작하고 무언가 손상된 동작이 없는지 자세히 조사한다.
•
보호 후 수정하기
◦
이 방식의 기본 개념은 소프트웨어를 변경할 때, 안전망을 이용하자는 것이다.
◦
안전망은 테스트 루틴을 코드의 곳곳에 적절히 배치하여 변경 작업 수행 후, 결과가 올바른지 확인하는 것이다.
•
이런 테스트를 작업 결과의 정확성을 보여주기 위한 테스트라 한다. 전통적으로는 회귀 테스트라고 부른다.
•
회귀 테스트란 주기적으로 테스트를 실행해 정상적인 동작 여부를 확인하고 소프트웨어가 동작하는지 조사하는 것이다.
•
변경 대상 코드의 주변에 배치된 테스트 루틴들은 소프트웨어 바이스의 역할을 한다.
◦
바이스란 목공예나 금속세공에서 공작물을 고정시키는 장치를 의미한다.
•
레거시 코드를 이용해 작업할 때 단위 테스트는 가장 중요한 요소 중 하나다. 시스템 수준의 회귀 테스트는 물론 중요하다. 하지만 소규모 수준의 부분별 테스트도 매우 중요하다.
◦
개발 과정에서 빠른 피드백을 제공함으로써 훨씬 더 안전한 리팩토링이 가능해지기 때문이다.
단위 테스트란?
•
단위 테스트의 기본 개념은 독립된 개별 소프트웨어 컴포넌트를 테스트하는 것이다.
•
그렇다면 컴포넌트는 무엇일까? 정의는 다양하지만 시스템의 가장 원자적인 동작 단위를 의미한다.
◦
절차적 프로그래밍에서의 단위는 보통 함수를 의미하며, 객체 지향 프로그래밍에서는 클래스를 의미한다.
•
함수나 클래스의 분리 테스트는 단위 테스트의 의미상 매우 중요하다. 대부분의 오류는 전체 시스템에서 일어나므로 대규모 테스트가 더 중요해 보일테지만 대규모 테스트는 몇 가지 문제가 있다.
◦
오류 위치 파악
▪
테스트 루틴이 테스트 대상으로 멀어질수록 테스트 실패가 의미하는 바를 파악하기 힘들어진다. 그 결과 실패가 발생한 위치를 특정하는데 오랜시간이 든다.
◦
실행 시간
◦
커버리지
▪
코드 조각과 그 코드 조각을 실행시키는 값들의 연결 관계를 파악하기 어렵다.
•
단위 테스트는 대규모 테스트의 단점을 보완할 수 있으며 조건은 다음과 같다.
◦
실행 속도가 빠르다.
◦
오류 위치 파악에 도움이 된다.
상위 수준의 테스트
•
단위 테스트도 중요하고 상위 수준의 테스트 역시 중요하다.
•
상위 수준의 테스트를 적절히 작성하였을 때, 개별 클래스에 대한 테스트 루틴 작성도 쉬워질 때가 많다.
테스트를 통한 코드 보호
•
이제 테스트를 이용해 보호 후 수정하기라는 방식으로 코드 변경을 해야하는 것도 알았다. 그럼 구체적으로 어떻게 해야하는 걸까?
•
가장 먼저, 변경을 가할 코드 주위에 테스트 루틴을 배치해 안전성을 높여야한다는 점이다.
•
InvoiceUpdateResponder 클래스의 getResponseText 메소드와 Invoice 클래스의 getValue 메소드를 변경하고 싶다고 하자. 이 두 개의 메소드가 변경점인 것이다.
•
테스트를 하려면 테스트 하네스 내에서 두 클래스의 인스턴스를 생성해야 한다.
•
Invoice 클래스는 내부에 의존성이 없지만 InvoiceUpdateResponder는 그렇지 않다. 이 의존성들을 위해 코드를 변경해야할까?
•
즉, 테스트하기 어려운 이유는 무언가에 직접 의존하기 때문이다. 이런 의존성을 제거하기 위해 인터페이스를 사용해볼 수 있다.
•
의존 관계를 제거함으로써 영향도가 높은 코드 변경의 안전성을 담보하면서 테스트 루틴을 작성할 수 있게 된다.
레거시 코드를 변경하는 순서
변경 지점을 식별한다
•
코드 변경을 수행할 지점은 소프트웨어 아키텍처와 밀접하게 연결되어 있다.
테스트 루틴을 작성할 위치를 찾는다
•
특정 변경에 대한 테스트 루틴을 어디에 작성하면 좋을지 판단해야 한다.
의존 관계를 제거한다
•
테스트를 작성할 때 가장 큰 장애물 중 하나는 의존 관계다.
•
의존 관계가 문제가 되는 가장 대표적인 경우는 테스트 하네스 내부에서의 인스턴스 생성과 메소드 실행이다.
•
레거시 코드를 다룰 때는 테스트 루틴을 배치하기 위해 의존 관계를 먼저 제거해야 한다.
테스트 루틴을 작성한다
변경 및 리팩토링을 수행한다
•
저자는 레거시 코드에 기능을 추가할 때 TDD를 권장한다.