본문 바로가기
📇기타

레거시 코드에서 테스트 코드 시작하기

by 캔 2023. 12. 1.

무진장 긴 메서드, 알 수 없는 변수 이름, 텅 빈 테스트 코드 디렉터리...ㅠ

 

대다수의 기업의 경우, 기존에 개발되어 있는 프로젝트를 인계받거나 처음으로 유지 보수 업무를 맡게 되면 보게 되는 코드의 특징이다. 이런 코드를 이제 수정하거나 변경해야 한다. 이 코드들을 건드리기 시작하면 무수히 많은 오류와 버그에 부딪히게 될 것을 우리는 알고 있다. 손대지 않으면 안 되냐고? 안 된다. 기업이나 고객은 늘 새로운 요구사항을 가지고 온다. 요구사항에 맞추어 수정이 일어나지 않는다면 우리는 더 이상 돈을 받을 수 없다. 테스트 코드가 없는 상황에서 수정은 항상 잠재적인 문제를 가지고 있고 문제가 발생하면 쉽게 해결되지 않아 야근으로 이어진다. 즉 테스트 코드 개발 환경에서 테스트와 배포는 항상 불안하기만 하다.

 

이번 글에서는 이런 막막했던 상황들을 겪어보면서 테스트 코드를 어떻게 하면 잘 시작할 수 있을까 고민하면서 쓰게된 글이다. 방대하고 지저분한 코드에 어떻게 테스트 코드를 도입할 수 있는지 적어보려고 한다. 구체적인 테스트 도구 사용법 등은 여기서는 다루지 않고 다음 번에 얘기해보려고 한다.

테스트 코드의 장점들

상황을 타개하기 위해 테스트를 시작하려고 하지만 레거시 환경에서는 막막하기만 하다. 그러나 포기해서는 안 된다.

테스트 코드가 주는 여러 장점들을 보면서 테스트 도입에 대한 결의를 다시 다져보자.

1. 코드에 대한 자신감 증대
- 리팩터링, 코드 변경에 대한 두려움을 줄여준다.

2. 테스트 비용 및 시간 절감
- 자동화되고 빠르게 실행되는 테스트 코드를 작성함으로써 테스트에 쏟는 시간과 비용이 줄어든다.

3. 코드에 대한 문서화 및 신규 개발자 이해도 향상
- 단언문, 테스트 이름은 이해에 도움

4. 코드 품질 향상
- "좋은 코드는 테스트를 짜기 쉽다." 모듈 간의 강결합이 줄어들기 때문이다.

 

가장 중요한 점은 코드를 요구사항 변경이나 버그 발생 등으로 인해 코드를 수정해야 할 때 그로 인한 사이드 이펙트 발생 시 빠르게 알 수 있게 된다는 점이다. 그렇기 때문에 마틴 파울러의 리팩터링에서도 테스트 코드 작성부터 시작한다.

리팩터링의 첫 단계는 항상 똑같다. 리팩터링 할 코드 영역을 꼼꼼하게 검사해줄 테스트 코드들부터 마련해야 한다.

- p.28, 마틴 파울러, <<리팩터링>>

 

테스트 코드의 단위는 메서드로

일반적으로, 테스트 코드를 작성한다고 할 때, 단위 테스트를 가리킨다. 단위 테스트는 테스트 할 수 있는 가장 작은 단위의 코드에 대해 독립적으로 진행하는 테스트를 말하는데, 메서드를 테스트 단위로 보고 테스트 한다고 생각하면 편하다. 메서드는 코드 상에서 이미 하나의 단위로 분리가 되어 있어 메서드 하나를 그대로 테스트 하면 된다. 그리고 메서드는 어떤 기능을 담고 있으므로 그 기능을 테스트하려면 그 메서드에 대한 테스트 코드를 작성하는 것이 의미적으로도 맞다.

 

메서드는 객체 지향 프로그래밍에서 사용되는 일종의 함수이다. 함수는 값을 입력하면 출력값이 반환되는데, 제대로 된 함수라면 입력 값을 넣었을 때 반환되는 출력값도 정상이어야 한다. 만약에 출력 값이 기대한 바와 다르다면 그 메서드의 기능이 잘못 동작하고 있다는 것을 의미한다. 이를 테스트에 적용해보면 올바른 입력값을 함수의 매개변수에 넣었을 때 함수가 기대되는 출력값을 반환하면 테스트가 통과하고 그렇지 않으면 테스트가 실패한다고 할 수 있다.

실제 테스트 코드를 작성해보기

단언문 만들기

우선, 입력값을 넣었을 때 반환(리턴)할 것으로 예상하는 값을 만들어 놓자. 그리고 메서드 호출 시 반환하는 값이 같은지 확인하는 단언문을 만든다. 이제 테스트 코드의 절반을 완성했다.

 

입력 값 만들기

다음으로, 메서드의 매개변수에 들어갈 값 또는 객체 만든다. 다시 말해, 테스트 대상이 되는 테스트 케이스를 만드는 것이다. 여기서 메서드의 매개변수를 적게 만들어야 할 이유가 발생한다. 테스트 케이스가 많아질수록 관리해야 할 변수들도 많아져서 코드가 복잡해질 수 있다. 그럴 땐 차라리 매개변수를 하나의 값 객체로 만들어 버리면 좋다.

 

그리고 나서 테스트 코드를 실행해보면 테스트 결과가 나온다. 실패한다면 성공하도록 수정하고 다시 테스트 성공하면 다른 케이스들을 확인한다. 이때 주로 경계가 되는 케이스의 값이나 그런 값으로 구성된 객체(엣지 케이스/코너 케이스)를 사용한다. 적절한 테스트 케이스들이 테스트를 통과하면 코드에 대한 자신감을 가져도 된다. 정상적인 케이스나 문제가 될 수 있는 케이스에 대한 동작을 수행하는지 확인했기 때문이다. 이제 격리된 메서드의 내용을 수정하고 테스트를 통과하는지 확인하는 과정을 통해서 안전하게 리팩터링이나 코드를 수정할 수 있다.

마치면서

테스트에 대한 구체적인 방법이 아니라 테스트를 어디서부터 시작할지 무엇을 테스트해야 할지 모를 때 방향을 제시하려고 글을 써보았다. 특히, 하나의 메서드가 너무 커서 코드 일부를 건드렸다가는 걷잡을 수 없는 문제가 생길 것 같을 때 그런 코드에 대해 테스트 코드를 어떻게 작성할 수 있을지에 대해 말하고자 하였다.

 

테스트 코드가 없는 코드를 만져야 한다면 일단 테스트 코드 작성을 시작하자. 좋은 테스트 코드는 차차 만들어가면 된다. 테스트 코드에 대한 여러 가지 팁들, 하지 말아야 할 것들은 인터넷에 너무 많이 있다. 하나의 메서드를 잡고 그 메서드에 입력되는 값과 반환되는 값을 준비하여 기대하는 값이 나오는지 테스트를 하면 될 것 같다. 그렇게 하고 나면 그 메서드를 리팩터링 할 수 있다. 원하는 대로 맘껏 메서드 추출을 하고 메서드가 많아지면 테스트 분리도 해보고 변수명도 바꿔보자. 그러고 나서 테스트 메서드를 통과한다면 그 리팩터링은 성공한 것이다.