테스트 주도 개발(Test-Driven Development, TDD)은 소프트웨어 개발에서 품질을 향상시키고 유지보수성을 높이는 데 도움이 되는 접근 방식입니다. 테스트를 작성하면서 코드를 개발하는 것은 신규 코드에 대해서는 쉽게 적용할 수 있지만, 레거시 코드에 대해서는 도전적인 과제일 수 있습니다.
이 글에서는 Python의 unittest
모듈을 사용하여 레거시 코드를 테스트하는 전략에 대해 살펴보겠습니다.
1. 테스트 가능한 코드의 분리
레거시 코드는 종종 복잡하고 강하게 결합된 형태일 수 있습니다. 이러한 코드를 테스트하기 위해서는 먼저 테스트 가능한 코드로 분리하는 것이 바람직합니다.
테스트 가능한 코드로 분리하기 위해선 다음과 같은 방법을 고려할 수 있습니다:
- 클래스나 함수 내의 코드 블록 분리: 코드 블록을 작은 함수로 추출하여 테스트하기 쉽게 만듭니다.
- 외부 의존성 제거: 레거시 코드가 외부 서비스, 데이터베이스 등에 강하게 의존할 경우, 의존성을 인터페이스로 추상화하여 테스트 환경에서는 목 오브젝트(Mock Object)를 사용하도록 변경합니다.
2. 테스트 케이스 작성
unittest
모듈을 사용하여 테스트 케이스를 작성합니다. 테스트 케이스는 테스트하려는 각각의 기능이나 동작을 나타내는 함수입니다.
import unittest
class LegacyCodeTestCase(unittest.TestCase):
def test_functionality_one(self):
# Arrange
# ...
# Act
# ...
# Assert
# ...
def test_functionality_two(self):
# Arrange
# ...
# Act
# ...
# Assert
# ...
if __name__ == '__main__':
unittest.main()
각 테스트 함수는 테스트할 기능의 로직을 포함하며, Arrange-Act-Assert 패턴을 따릅니다. 테스트의 시작 시나리오를 설정하고(Arrange), 테스트 대상에 대한 동작을 수행하고(Act), 기대 결과를 확인(assert)합니다.
3. 리팩토링과 재테스트
레거시 코드를 분석하고 테스트를 작성하는 과정에서, 코드를 개선하거나 리팩토링하는 것이 필요할 수 있습니다. 테스트 케이스를 작성한 후에는 코드를 리팩토링한 뒤, 변경된 코드가 여전히 기능을 제대로 수행하는지 확인하기 위해 재테스트해야 합니다.
리팩토링을 진행할 때에는 기존 코드를 망치치 않는지 확인하기 위해 테스트 케이스가 반드시 통과하는지 확인하는 것이 중요합니다.
4. 코드 커버리지 측정
테스트 케이스 작성 후에는 코드 커버리지를 측정해보는 것이 좋습니다. 코드 커버리지는 테스트 케이스가 얼마나 많은 코드를 실행했는지를 측정하여 테스트의 완성도를 판단하는 지표입니다.
unittest
모듈에서는 coverage
패키지를 이용하여 코드 커버리지를 측정할 수 있습니다. 코드 커버리지를 측정하여 테스트가 얼마나 빠짐없이 전체 코드를 테스트하는지 확인하고, 테스트 케이스를 보완하거나 누락된 부분을 추가로 테스트할 수 있습니다.
5. 더 나아가기
레거시 코드를 효과적으로 테스트하기 위해서는 위에서 언급한 전략뿐만 아니라 다양한 기법과 패턴을 활용할 수 있습니다. 유닛 테스트를 지속적으로 도입하여 코드 품질을 향상시키기 위해 계속해서 공부하고 개선해 나가야 합니다.
프로젝트는 계속 발전하고 변화하며, 레거시 코드도 새로운 기능과 요구사항을 지원해야 할 때가 있습니다. 유지보수를 위해 테스트 코드를 작성하는 것은 레거시 코드에 대한 이해도 향상과 함께 시간과 비용을 절약하는 데 도움이 됩니다.