요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

지금 돌이켜 생각하면 부끄러운 일이지만, 처음 테스트 코드를 마주했을 때 든 생각은 '왜 귀찮은 테스트 코드를 만들어야 하는 걸까?'였습니다. 물론 지금은 테스트 코드의 중요성을 깨달아 열렬한 팬이 되었고, 이를 제 사회 생존을 위한 필수 장치로 여기며 함께하고자 노력하고 있습니다. 요즘은 대부분의 개발 과정에서 테스트 코드 작성을 실천하며 더 나은 결과물을 만들고 있습니다. 이번 글에선 테스트 코드가 무엇이고, 왜 중요한지에 대해 소개하고자 합니다.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

테스트 코드는 왜 만들까?

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인

 

지금 돌이켜 생각하면 부끄러운 일이지만, 처음 테스트 코드를 마주했을 때 든 생각은 '왜 귀찮은 테스트 코드를 만들어야 하는 걸까?'였습니다. 물론 지금은 테스트 코드의 중요성을 깨달아 열렬한 팬이 되었고, 이를 제 사회 생존을 위한 필수 장치로 여기며 함께하고자 노력하고 있습니다. 요즘은 대부분의 개발 과정에서 테스트 코드 작성을 실천하며 더 나은 결과물을 만들고 있습니다. 이번 글에선 테스트 코드가 무엇이고, 왜 중요한지에 대해 소개하고자 합니다.

 

테스트 코드란 무엇인가

테스트 코드(Test code)는 소프트웨어의 기능과 동작을 테스트하는 데 사용되는 코드입니다. 소프트웨어 테스트는 소프트웨어의 결함을 찾아내고 수정하는 과정에서 매우 중요합니다. 테스트 코드는 개발자가 작성한 코드를 실행하고 예상된 결과가 나오는지 확인하는 데 사용됩니다.

 

일반적으로 각 단계의 테스트 코드는 V 모델(V-Model)을 통해서 살펴볼 수 있습니다. V 모델은 소프트웨어 개발 생명 주기(Software Development Life Cycle, SDLC) 중 하나로, 시스템 개발 과정을 시각화한 모델입니다. 

 

테스트 코드에는 단위 테스트(Unit Testing), 통합 테스트(Integration Testing), 시스템 테스트(System Testing), 사용자 인수 테스트(User Acceptance Testing) 등 다양한 종류가 있으며, 각각의 테스트는 특정한 측면에서 소프트웨어를 평가합니다. 모든 테스트는 기대한 입력값과 출력값을 반환하는지 확인합니다. 구분하는 이유는 단지 테스트의 대상의 범위가 다르기 때문입니다.

 

테스트코드
<출처: software-engineering-sdlc-v-model>

 

테스트 코드를 작성하면 개발자는 소프트웨어가 어떻게 작동하는지를 이해하고, 소프트웨어를 수정할 때 예상치 못한 부작용을 방지할 수 있습니다. 또한 테스트 코드는 개발자 간의 협업을 원활하게 하고, 소프트웨어를 유지 보수하는 데 필요한 문서화 작업을 줄일 수 있습니다.

 

일반적으로 개발자는 단위 테스트, 통합 테스트와 관련된 테스트 코드를 주로 다룹니다. 간단하게 단위 테스트와 통합 테스트에 대해 살펴보겠습니다.

 

1) 단위 테스트란?

단위 테스트(Unit Test)는 소프트웨어 개발에서 일반적으로 사용되는 테스트 중 하나로, 개별적인 코드 단위(보통 함수, 메서드)가 의도한 대로 작동하는지 확인하는 과정입니다. 소프트웨어의 개별 코드 단위를 테스트하여 오류를 발견하고, 이를 수정하여 전체적인 소프트웨어의 품질을 향상시키는 과정입니다. 이를 위해서는 테스트 케이스를 작성하여, 각각의 코드 단위가 정확한 입력값과 출력값을 반환하는지 확인합니다.

 

단위 테스트 작성을 위해서는 다양한 라이브러리와 프레임워크를 제공합니다. 국내에서 가장 많이 사용하는 언어인 자바(Java)에서는 대표적으로 Junit이 있습니다.

 

단위테스트
<출처: 본인>

 

위 예제 코드에서 Calculator 클래스는 add 메서드를 가지고 있습니다. add 메서드는 두 개의 정수를 더한 결과를 반환합니다.

 

CalculatorTest 클래스는 Junit의 @Test 어노테이션을 이용하여 testAdd 메서드를 선언합니다. testAdd 메서드는 Calculator 클래스의 add 메서드가 제대로 작동하는지를 검증하는 코드를 포함합니다.

 

assertEquals 메서드를 이용하여 testAdd 메서드에서 Calculator 클래스의 add 메서드가 반환한 값이 예상한 값과 같은지를 비교합니다. 만약 값이 다르다면 테스트가 실패하고, 같다면 테스트가 성공합니다.

 

 

2) 통합 테스트란?

통합 테스트(Integration Test)는 서로 다른 모듈들 간의 상호작용을 테스트하는 과정입니다. 예를 들어, 신규로 개발한 API 서버 내의 DB 호출 함수가 데이터베이스의 데이터를 잘 호출하고 있는지 테스트하는 과정이라고 생각하면 됩니다.

 

통합 테스트는 보통 모듈 간 인터페이스 테스트, 시스템 레벨 테스트 등의 방법으로 수행됩니다. 예를 들어, 여러 개의 모듈이 연결된 백엔드 API 웹 애플리케이션의 경우에는 서로 다른 모듈들 간의 상호작용을 테스트하기 위해 각 모듈 단위 테스트를 모두 완료한 뒤, 둘 이상의 모듈을 거쳐서 동작하는 API 테스트 시나리오를 기반으로 통합 테스트를 수행합니다. 여기서 모듈은 웹 서버, WAS, DBMS, 메시지 브로커(Message Broker), 파일 서버(File Server) 등이 될 수 있습니다.

 

통합테스트
<출처: 통합 테스트 - wikimedia>

 

단위 테스트에 비하면 통합 테스트는 작성하기가 어려운 편입니다. 모듈 간의 상호작용을 테스트하기 때문에 각기 다른 모듈의 설정 방법을 알아야 하고, 테스트를 다시 수행할 수 있는 깨끗한 환경으로 되돌려놔야 하는 경우도 있습니다. 또한 테스트에 영향을 끼치는 요인이 데이터와 로직뿐만 아니라 통신구간이나 해당 모듈의 환경설정 정보 등이 더 많습니다.

 

따라서 통합 테스트를 수행할 때는 더 많은 리소스와 시간이 필요하며, 오류를 발견하고 수정하는데 보다 많은 노력이 필요합니다. 그러나 통합 테스트를 수행함으로써, 전체적인 소프트웨어 시스템의 신뢰성과 안정성을 높일 수 있습니다.

 

통합 테스트를 GUI 환경에서 사용할 수 있는 도구로는 대표적으로 Selenium, Postman, Apache JMeter 등이 있습니다. 물론 Junit, Jest, Pytest 등 테스트 라이브러리를 이용해 코드 기반으로 작성할 수도 있습니다.

 

통합테스트 예시
<출처: Postman 테스트 스크립트 작성>

 

통합 테스트 예시
<출처: Postman 테스트 시나리오와 통합 테스트 수행>

 

그래서 테스트 코드를 왜 사용해야 할까?

테스트 코드를 사용하면 다양한 상황에서 얻게 되는 '자신감'이 가장 큰 장점입니다. 이러한 자신감은 테스트 코드 작성 과정에서 자연스럽게 경험하는 여러 긍정적 요소로 얻어지게 됩니다. 제가 생각하는 긍정적 요소들은 아래와 같습니다.

 

  • 내가 무엇을 만들고 있는지 정확히 인지: 테스트 코드 작성을 통해 요구사항의 기능적인 항목들을 정리하고 코너 케이스를 찾게 되며, 이는 문서의 역할을 수행합니다.
  • 리팩토링을 진행할 때 부담 덜어주기: 테스트 코드가 있으면 코드 수정 후에도 기능이 정상적으로 작동하는지 검증할 수 있어, 개발자는 불안감에서 벗어날 수 있습니다.
  • 결합도와 의존성이 낮은 코드를 지향: 테스트 코드 작성을 통해 의존성이 높은 부분을 개선하면 프로젝트의 코드 품질이 향상되고 개발자로서의 경험이 쌓입니다.

 

1) 무엇을 만들고 있는가?

개발자는 주로 요구사항을 분석하여 설계 후 구현하는 일을 합니다. 가장 중요한 것은 요구사항을 제대로 분석하는 일입니다. 그리고 반드시 요구한 항목을 최대한 구현해야 합니다. 아무리 열심히 만들어도 요구하지 않는 것을 만드는 건 무의미합니다.

 

테스트 코드를 작성하면 요구사항의 기능적인 항목들을 차분히 정리하는 과정을 경험할 수 있습니다. 이때 발생할 수 있는 예상외의 코너 케이스(Corner case)를 찾을 수도 있고요.

 

개발자 짤
<출처: instagram _compiler_>

 

이렇게 작성한 테스트 코드는 문서의 역할도 수행합니다. 테스트 코드를 통해 개발자는 해당 기능에 필요한 입력값과 출력값을 파악할 수 있으며, 실패 케이스를 처리하는 테스트 코드를 살펴보면서 예상 동작을 명확하게 이해할 수 있습니다. 테스트 코드가 예상 동작을 상호 간에 명확하게 다루기 때문에, 이를 계약(Contract)이라고 부르기도 합니다.

 

 

2) 고치기 쉬운 환경으로

테스트 코드가 없는 프로젝트에서는 새로운 기능을 추가하는 것도 어렵지만, 기존 기능을 수정하는 것은 더욱 어려운 일이 됩니다. 그 이유는 수정하는 개발자가 아닌 다른 사람이 만든 코드이기 때문에, 작성 시점의 요구사항과 예외 케이스에 대해 정확한 이해가 어렵기 때문입니다. 초기 요구사항을 담고 있는 명세서를 철저하게 검토한다 해도, 기존 기능의 코드가 그 요구사항을 완전히 충족하는지 확인하기 어렵습니다.

 

게다가 제 경험에 따르면, 대부분의 기존 기능을 수정하는 작업은 긴급하게 처리해야 하는 경우가 많습니다. 따라서 많은 개발자들은 제한된 시간 내에 초기 요구사항과 기존 코드를 분석하고 수정된 코드를 적용하는 작업에 큰 부담을 느낍니다. 그럼에도 불구하고 수정 작업은 필요하기 때문에 어떤 경우에는 "수정하고 기도하기"라는 전략을 사용하게 됩니다.

 

“시스템을 변경하는 방법에는 크게 두 가지가 있다. 첫째 수정 후 기도하기, 둘째 보호 후 수정하기” -  레거시 코드 활용 전략

 

레거시 코드 활용 전략
<출처: Working effectively with legacy code>

 

이럴 때 테스트 코드가 있다면 코드 수정이나 구조 변경 후에도 기능이 요구사항에 맞게 정상적으로 작동하는지 검증할 수 있게 됩니다. 이로 인해 개발자는 더 이상 ‘수정하고 기도하기’ 같은 불안감에서 벗어날 수 있습니다. 개발자는 부담 없이 여러 번의 테스트를 통해 기존 기능이 제대로 작동하는지 빠르게 확인할 수 있으며, 이를 통해 더 좋은 품질의 개발을 할 수 있게 됩니다.

 

 

3) 더 나은 코드를 위해

테스트 코드가 없는 프로젝트에서 테스트 코드를 작성하다 보면 마주치는 문제들이 있습니다. 특히 단위 테스트를 작성하는 과정에서 이러한 문제들을 더 자주 경험하는데요. 그 문제는 바로 클래스와 메서드 또는 함수 간에 복잡하게 얽혀 있는 의존성으로 인해 테스트 코드를 작성하기 어렵다는 점입니다.

 

의존성이 높아 단위 테스트를 작성하기 어려운 경우로 설명하겠습니다. 아래는 Java와 JUnit을 사용한 코드 예시입니다.

 

자바 코드 예시
<출처: 본인>

 

예를 들어, UserService 클래스가 EmailService 클래스에 강한 의존성을 가지고 있다고 가정해 보겠습니다. 위 코드에서 UserService 클래스의 signUp 메서드를 단위 테스트하려고 시도하면, EmailService 클래스의 sendEmail 메서드가 실제로 이메일을 보내게 됩니다. 이러한 상황은 단위 테스트를 작성하기 어렵게 만듭니다.

 

하지만 이 문제를 해결하기 위해 의존성 주입(Dependency Injection)을 사용하여 의존성을 느슨하게 만들어볼 수 있습니다. UserService 클래스의 필드로 선언하고, 생성자를 통해 EmailSender를 주입받도록 의존성 주입을 구현할 수 있습니다.

 

자바 코드 예시
<출처: 본인>

 

이제 UserService 테스트를 작성할 때, EmailSender 인터페이스의 구현을 가짜 객체(Mock)로 교체하여 실제 이메일 전송을 하지 않고 테스트를 진행할 수 있습니다.

 

자바 코드 예시
<출처: 본인>

 

의존성으로 인해 테스트 코드를 작성하기 힘든 부분은 프로젝트의 코드에서 문제 발생 가능성이 높은 영역이기도 합니다. 높은 의존성은 프로젝트가 커질수록 코드 수정과 신규 기능 추가에 점차 많은 시간과 비용이 소요되며, 리스크도 함께 증가합니다. 따라서 이런 영역을 개선하기 위해 노력한다면 프로젝트의 코드 품질이 향상되고, 개발자 개인에게도 고품질의 코드 개선 경험을 쌓을 수 있는 소중한 기회가 됩니다.

 

그러나 이 문제는 개발자 혼자 해결하기에는 시간이 부족할 수도 있습니다. 그래서 프로젝트를 관리하는 관리자나 조직 차원에서의 투자가 필요할 수 있습니다.

 

 

4) 테스트 코드의 장단점

지금까지의 내용을 정리하면, 테스트 코드를 사용할 때 다음과 같은 장점이 있습니다.

 

장점

  • 코드 품질 향상:테스트 코드를 작성하면 소프트웨어의 품질을 향상시킬 수 있습니다. 테스트 코드를 통해 버그를 사전에 찾아내고 수정할 수 있으며, 이는 개발자가 신뢰할 수 있는 코드를 작성할 수 있게 도와줍니다.
  • 회귀 테스트:코드 변경이나 업데이트 시, 기존 기능이 올바르게 작동하는지 확인할 수 있습니다. 이를 통해 의도치 않은 결과나 버그를 빠르게 발견하고 수정할 수 있습니다.
  • 문서화: 테스트 코드는 개발자가 기능의 동작 방식을 이해하는 데 도움이 되는 문서로 작용할 수 있습니다. 테스트 코드를 통해 코드의 예상 동작을 명확하게 확인할 수 있어, 개발자 간의 커뮤니케이션 향상에도 도움이 됩니다.
  • 리팩토링 지원: 테스트 코드가 존재하면 코드 수정이나 구조 변경에 대한 두려움을 줄일 수 있습니다. 테스트 코드를 통해 리팩토링 후에도 기능이 정상적으로 작동하는지 확인할 수 있기 때문입니다.

 

그러나 테스트 코드에도 장점만 있는 것은 아닙니다. 테스트 코드를 작성하려면 시간이 들고, 이를 관리하는 시간과 비용도 필요합니다. 또한 프로젝트의 규모와 만드는 시스템의 수명을 함께 고려해야 합니다. 

 

단점

  • 개발 시간 증가:테스트 코드를 작성하면 개발 시간이 증가할 수 있습니다. 이는 초기 개발 비용이 증가하는 요인이 될 수 있으나, 장기적으로는 버그 수정 및 유지 보수 비용 절감으로 이어질 수 있습니다.
  • 불완전한 테스트: 모든 시나리오를 고려한 테스트 코드를 작성하기 어려울 수 있습니다. 이로 인해 일부 버그가 테스트를 통과하게 되어, 완전한 테스트가 어려운 경우가 있습니다.
  • 오버 엔지니어링: 테스트 코드 작성에 과도한 시간과 노력을 투자하게 되면, 오버 엔지니어링으로 이어질 수 있습니다. 이는 개발 속도 저하와 비효율적인 리소스 사용을 초래할 수 있습니다.
  • 유지 보수 비용:테스트 코드 역시 유지 보수가 필요한 코드입니다. 소프트웨어의 기능이 변경되거나 확장될 때마다 테스트 코드도 함께 수정해야 합니다. 이로 인해 추가적인 유지 보수 비용이 발생할 수 있습니다.
  • 학습 곡선:테스트 프레임워크와 테스트 작성 방법을 익히는데 시간이 필요합니다. 팀 구성원이 테스트 작성에 익숙하지 않은 경우, 학습과 적용에 시간이 소요되며 이로 인해 개발 속도가 저하될 수 있습니다.

 

개인적으로 테스트 코드의 단점보다 장점이 더 크게 작용한다고 생각합니다. 시스템을 유지 보수하는데 드는 전체 시간과 노력을 고려하면, 테스트 코드 작성에 소요되는 시간과 노력의 비중은 크지 않다고 느껴집니다.

 

 

마치며

테스트 코드를 통해 모든 버그를 방지할 수 있는 것은 아닙니다. 그저 약간의 도움을 줄 뿐입니다. 오히려 그런 맹신은 프로젝트의 진행과 품질 결과에 더 위험한 영향을 미칠 수 있습니다. 제가 강조하고 싶은 테스트 코드 사용 이유는 ‘더 나은 개발자가 되기 위한 좋은 기회와 습관’을 제공한다는 것입니다. 저 또한 테스트 코드를 작성하는 과정에서 많은 도움을 받고 있습니다. 여러분도 기회가 된다면 테스트 코드의 장점을 한 번쯤은 경험해 보시길 바랍니다.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
백엔드 개발자
16
명 알림 받는 중

작가 홈

백엔드 개발자
16
명 알림 받는 중
IT 컨설턴트 출신으로 지금은 백엔드 개발자로 일하며 경험을 쌓고 있습니다. 아주 작은 기업부터 외국계 글로벌 기업까지의 조직문화 경험이 있습니다. 건강한 문화를 전파하고, 개인의 성장에 자극이 될 수 있는 글과 이야기를 좋아합니다.

좋아요

댓글

스크랩

공유

공유

요즘IT가 PICK한 뉴스레터를 매주 목요일에 만나보세요

요즘IT가 PICK한 뉴스레터를
매주 목요일에 만나보세요

뉴스레터를 구독하려면 동의가 필요합니다.
https://auth.wishket.com/login