단위/통합/기능 테스트

 

Summry

본 문서에서는 단위 테스트(Unit Test) 통합 테스트(Integration Test) 기능 테스트(Functional Test)에 대해 정리한다.

send me email if you have any questions.


단위 테스트(Unit Test)

단위 테스트는 소스코드의 특정 모듈(프로그램 내 하나의 기능을 부르는 말)이 의도된 대로 정확히 작동하는지 검증하는 절차이며, 함수, 메서드, 개별 코드 같은 작은 단위에 대해 테스트 케이스(Test Case)로 분리하고 테스트 코드를 작성하여 테스트하는 것을 말한다.

외부 API와의 연동이 필수라든가 DB 데이터 접근 등 외부 리소스를 직접 사용해야 하는 테스트라면 단위 테스트가 아니다. 단위 테스트에서 외부와의 연동이 필요하다면 테스트 대역(Test Double)을 사용하면 된다.

테스트 대역(Test Double)??
외부 의존 구성요소를 사용할 수 없거나 직접 사용하고 싶지 않을 때, 테스트 대상 코드와 대신해서 상호작용하는 객체를 말한다.

  • 언어별로 단위 테스트를 위한 프레임워크가 존재하며 보통 이름을 xUnit이라 칭한다.
    • ex) JUnit(JAVA), CppUnit(C++), NUnit(.NET 프레임워크), unittest(Python)

단위 테스트 수행 이유

  1. 기능 테스트 수행 시간 단축
    • 기능 테스트는 비용이 많이 든다. 일반적으로 애플리케이션을 열고 사용자(또는 다른 사용자)가 예상되는 동작의 유효성을 검사하기 위해 따라야 하는 일련의 단계 수행이 포함된다. 이러한 단계는 항상 테스터에게 알려진 것은 아니며, 이는 테스트를 수행하기 위해 해당 영역에 더 많은 지식을 갖추어야 함을 의미한다. 테스트 자체는 사소한 변경인 경우 몇 초가 걸리거나 큰 변경의 경우에는 몇 분 정도 걸릴 수 있다. 마지막으로, 이 프로세스는 시스템에서 수행하는 모든 변경 사항에 대해 반복되어야 한다.
    • 반면에 단위 테스트는 밀리초 단위의 작은 시간이 소요되고 단추를 눌러 실행할 수 있으며 시스템 전체에 대한 정보가 반드시 필요하지는 않는다. 또한 테스트 통과 또는 실패 여부는 개인이 아닌 test runner의 몫이다.
  2. 회귀에 대한 보호
    • 테스터는 새 기능을 테스트할 뿐만 아니라 이전에 구현된 기능이 여전히 예상대로 작동하는지 확인하기 위해 이전에 존재했던 기능도 테스트하는 것이 일반적이다.
    • 단위 테스트를 사용하면 모든 빌드 후에 또는 코드 줄을 변경한 후에도 전체 테스트 도구 모음을 다시 실행할 수 있다. 즉, 새 코드가 기존 기능을 중단시키지 않는다는 신뢰를 준다.
  3. 실행 가능한 설명서
    • 특정 메서드가 무엇을 하는지 또는 특정 입력이 지정된 동작이 어떻게 수행되는지 항상 명확하지는 않을 수 있다. 빈 문자열을 전달하면 이 메서드는 어떻게 작동하는가를 자문해 볼 수 있다.
    • 이름이 잘 지정된 단위 테스트의 도구 모음이 있는 경우 각 테스트는 지정된 입력에 대해 예상되는 출력을 명확하게 설명할 수 있어야 한다. 또한 실제로 작동하는지 확인할 수 있어야 한다.
  4. 낮은 결합도 코드
    • 코드가 밀접하게 결합되면 단위 테스트하기가 어려울 수 있다. 따라서 작성 중인 코드에 대한 단위 테스트를 만들지 않으면 결합이 덜 분명해질 수 있다.
    • 코드 테스트를 작성하면 결합은 자연스럽게 분리된다. 그렇지 않으면 테스트하기가 더 어려워지기 때문.

장점

  1. 테스트 시간 단축으로 문제점 발견 가능성이 높아지고 안정성이 향상
    • 단위 테스트의 목적은 프로그램의 각 부분을 고립시켜서 각각의 부분이 정확하게 동작하는지 확인하는 것이다. 이를 통해 문제 발생 시 정확하게 어느 부분이 잘못되었는지를 재빨리 확인할 수 있게 해 준다. 따라서 프로그램의 안정성이 높아진다. 단위 테스트는 개발 시간을 증가시키는 것처럼 보이지만 개발 기간 중 대부분을 차지하는 디버깅 시간을 단축시킴으로써 여유로운 프로그래밍을 가능케 한다.
  2. 이전 기능도 함께 테스트하기 때문에 더 쉬워진 코드 변경
    • 프로그래머는 단위 테스트를 믿고 리팩토링을 할 수 있다. 리팩토링 후에도 해당 모듈이 의도대로 작동하고 있음을 단위 테스트를 통해서 확신할 수 있다. 이를 회귀 테스트(Regression Testing)라 한다. 어떻게 코드를 고치더라도 문제점을 금방 파악할 수 있고 수정된 코드가 정확하게 동작하는지 쉽게 알 수 있게 되므로 프로그래머들은 더욱더 의욕적으로 코드를 변경할 수 있게 된다.
  3. 테스트 간 결합도가 낮으므로 간단해진 통합
    • 단위 테스트는 단위 자체의 불확실성을 제거해주므로 상향식(Bottom-up) 테스트 방식에서 유용하다. 먼저 프로그램의 각 부분을 검증하고 그 부분들은 합쳐서 다시 검증하는 통합 테스트에서 더욱더 빛을 발한다.

좋은 단위 테스트

  • Fast
    • 완성도 높은 프로젝트에서 수천 개의 단위 테스트를 수행하는 것은 드문 일이 아니다. 단위 테스트는 실행하는 데 시간이 거의 걸리지 않는다. (밀리초단위..)
  • Isolated
    • 독립형 단위 테스트는 독립적으로 실행될 수 있으며, 파일 시스템 또는 데이터베이스와 같은 외부 요인에 종속되지 않는다.
  • 반복 가능
    • 단위 테스트를 실행하는 것은 해당 결과와 일치해야 한다. 즉, 실행 사이에 아무 것도 변경하지 않으면 항상 동일한 결과를 반환한다.
  • 자체 검사
    • 테스트는 사람의 개입 없이 통과했는지 여부를 자동으로 검색할 수 있어야 한다.
  • Timely
    • 단위 테스트는 테스트 중인 코드에 비해 작성하는 데 불균형적으로 긴 시간이 걸리지 않아야 한다. 코드를 작성하는 데 비해 많은 시간이 걸리는 코드를 테스트하는 경우 더 많은 테스트가 가능한 디자인을 고려할 필요가 있다.

통합 테스트(Integration Test)

통합 테스트는 모듈을 통합하는 과정에서 모듈 간 인터페이스가 올바르게 작동하는지를 테스트하는 것을 말한다.

  • 일반적인 웹 어플리케이션은 프레임워크, 라이브러리, 데이터베이스, 구현한 코드가 주요 통합 테스트 대상이다.
    • 예를 들어 회원 가입 코드에 대한 통합 테스트를 수행하면 스프링 프레임워크나 마이바티스 설정이 올바른지, SQL 쿼리문이 맞는지, DB 트랜잭션이 잘 동작하는지 등을 검증할 수 있다.
  • 여러 모듈간 통합을 거치기 때문에 단위 테스트에 비해 테스트 실행 속도가 느린 편이다.

트랜잭션(Transactcion)?
데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위를 뜻한다.

기능 테스트(Functional Test)

기능 테스트는 사용자와 어플리케이션의 상호작용이 원활하게 이루어지는지 테스트하는 것을 말한다.
그래서 이 테스트를 수행하려면 시스템을 구동하고 사용하는데 필요한 모든 구성요소가 필요하다.

따라서 E2E(End to end) 테스트로도 볼 수 있다. 예를 들면 브라우저에 웹 서버를 구동하거나 모바일에 앱을 구동하고 화면의 흐름에 따라 알맞은 상호 작용을 해야 하는 것이다.

정리

위의 세 가지 테스트 유형은 모두 상관 관계가 있다. 특정 테스트만을 수행하는 것이 아닌 모든 테스트를 상황에 맞게 수행하여 개발자가 의도하지 않은 동작이나 버그를 잡아내야 한다.

Reference

.NET Core 및.NET 표준을 사용하는 단위 테스트 모범 사례 - Microsoft
JUnit의 개념과 장점, 단위 테스트의 개념과 장점, 단위/통합/기능 테스트 - 으뜸별