본문 바로가기

.Net Technology/.NET TDD

(4) NUnit 시작하기 - 유닛테스트와 프레임워크

앞에서 이미 통합 테스트(Integrate Test)와 유닛 테스트(Unit Test)의 차이가 무엇인지 그리고 왜 유닛 테스트가 중요한지에 대해서 설명했다. 이번 장에서는 보다 자세히 유닛 테스팅이 무엇인지 살펴보고 또한 NUnit을 이용해서 테스트 코드를 작성하는 방법을 살펴보도록 하겠다. 
 
유닛 테스트의 정의
 
먼저 유닛 테스트를 간단하게 정의하자면 하나의 프로그램 유닛을 테스트 할 수 있는 단위로 정의 할 수 있다. 여기서 하나의 프로그램 유닛이라는 것은 하나의 기능을 가지고 있는 메서드 정도가 될 수 있다. 앞에서 1부터 20까지의 수 중에서 3의 배수와 5의 배수일 때 특정 문자열을 반환하는 예제를 살펴봤었다. 여기서 우리는 for문 안에서 몇 번을 반복하든지 크게 상관하지 않았다. 중요한 것은 해당 수에 알맞은 문자열을 반환하는 것이 우리의 목표였고 하나의 유닛이었던 것이다. 만약 우리가 For문이 제대로 1부터 20까지 반환하는지도 한 메서드 안에서 같이 정의 했다면 이 프로그램은 이미 유닛의 범위를 넘어서게 된다. 
 
단, 여기서 하나의 유닛이 존재한다고 하더라도 테스트 요소는 여러 개일 수 있다. 우리도 이미 앞의 예제에서 하나의 메서드를 가지고 4개 이상의 테스트를 작성했다. 물론, 위의 예제가 굉장히 간단해서 그럴 수도 큰 고민 없이 작성했을 수도 있다. 만약 그 메서드에서 다른 클래스를 참조 해서 사용하고 있다면 어떨까? 우리는 이렇게 서로 다른 객체들이 서로 참조관계를 가지고 있는 것을 가지고 의존성(Dependency)이라고 부른다. 이 경우에 우리는 의존성을 제거하나 하나의 유닛만 테스트 해야 하는데 이 부분에 대해서는 뒷부분에서 보다 자세히 살펴 볼 것이다.
 
하나의 유닛을 정의하는데 있어서 무엇보다도 중요한 것은 유닛 테스트의 핵심이 바로 향후에 어떤 에러가 있을 때 어느 유닛에서 문제가 있는지 정확하게 잡아내는 것에 있다는 것이다. 
 
 
모호한 테스트 범위들
 
먼저 처음 유닛테스팅을 시작할 때 가장 모호한 부분이 바로 지금 테스트 하고 또 작성해야 될 코드가 다른 시스템과 연결되어 있을 경우이다. 예를 들면 다른 DLL을 참조하여 사용할 수도 있고 DB나 웹서비스와 같이 외부 시스템들과 연결되어 있는 경우가 다분하다. 만약 테스트를 진행했는데 이러한 요소들과의 연결부분에서 에러를 발생할 가능성이 있기 때문에 우리는 실제로 내부적인 문제를 진단하는데 큰 어려움이 겪게 된다. 앞에서 유닛 테스트의 핵심은 모든 테스트를 유닛 별로 자동화시켜 놓은 뒤에 어떤 문제가 발생했을 때 그 유닛을 정확하게 잡아내는 것에 있다고 했다. 즉, 우리는 여러 레이어들을 일일이 진단하면서 다시 그 버그를 잡는 작업이 진행된다면 그것은 이미 유닛 테스트의 목적을 상실하게 되는 것이다.
 
다른 또 하나의 이슈는 DB와 같이 데이터를 공유하여 사용하다 보면 다른 개발자들에 의해서 그 라이브러리가 수정되거나 업데이트 되거나 할 경우에 에러가 발생할 수 있다. 즉, 테스트 결과는 외부의 리소스에 따라서 달라질 수 있다는 것이다. 다시 유닛 테스트의 정의로 돌아와서 만약 하나의 값을 데이터 베이스에 저장한 뒤에 읽어오고 또 지우는 테스트가 포함된다면 이것은 유닛 테스트의 범위를 벗어 나게 되는 것이다. 효율 면으로 살펴봤을 때도 외부 라이브러리와의 연동이 유닛 테스트가 될 수 없는 이유도 분명하다. 만약 나 혼자서만 그 DB를 사용하는 것이 아니라 다른 개발자들도 그 DB의 같은 테이블을 사용하고 있다고 가정해보자. 이 경우에 여러 개발자가 같은 테스트를 작성하게 될 확률이 높다. A모듈에서도 그 테이블에 값을 불러 올 수도 있고 B 모듈에서도 그 테이블에 값을 불러 올 수 있기 때문인 것이다. 
 
그렇다면 이런 경우의 테스트는 어떻게 진행해야 될 것인가? 이 경우에 우리는 통합 테스트 코드를 작성해야 한다. 통합 테스트 역시 개발 중에 작성되는 테스트로서 유닛테스트와 크게 다르지 않다. 통합 테스트에 있어서 좋은 모토는 “일찍 통합하여 자주 통합하자” 이다. 이 통합 테스트는 뒷부분의 챕터에서 보다 자세히 살펴보도록 하겠다. 
 
 
유닛 테스트 프레임워크
 
유닛 테스트 프레임워크가 존재하기 전에 개발자들은 자동적으로 점검이 가능한 테스트를 작성하는데 있어서 큰 어려움이 있었다. 초기에는 실제로 테스트 팀에서 각 테스트 별로 직접 윈도우를 만들고 또 버튼을 추가해서 테스트를 진행하고 했었다. 이 테스트 결과로 메시지 박스를 보여주거나 텍스트를 뿌려서 보여주고는 했다. 하지만 이렇게 진행해오던 테스트들이 지금은 비주얼 스튜디오나 별도의 테스트 도구를 통해서 어떤 추가적인 개발 없이 각각의 테스트를 자동화 시키는 것이 가능해졌다. 애자일의 개발방법론에서 매일 빌드하여 배포하고 테스트를 자동화하는 CI(Continuous Integration)을 도입이 가능해진 이유도 바로 이렇게 발전된 유닛 테스트 프레임워크 툴이 존재하기 때문이기도 하다. 
 
NUnit이나 MSTest와 같은 테스트 프레임워크들이 바로 그 역할들을 수행하게 된다. 유닛 테스트 프레임워크들은 일관된 코딩 스타일을 제공하고 있는데 단순히 테스트 메서드와 테스트 클래스들 위에 애트리뷰트를 적용하면서 간단하게 코드를 작성할 수 있다. 즉, 과거처럼 테스트 UI를 직접 작성하거나 에러메세지를 작성하여 보여주지 않아도 되는 것이다. 비주얼 스튜디오에서는 테스트에 실패하게 될 경우에 왜 테스트가 잘못 되었는지 메시지를 보여주게 된다. 
 
[그림1] 비주얼 스튜디오 2013에서의 테스팅 메시지
 
 
NUnit 프레임워크는 현존하는 프레임워크들 중에 가장 많이 사용되고 또 이용되고 있다. NUnit은 닷넷 테스트 툴로 제공되고 있으며 자바를 위한 프레임워크로 JUnit이라는 프레임워크 또한 제공하고 있다. 기존에는 비주얼 스튜디오가 별도로 테스트 툴을 제공하지 않았기 때문에 아래 [그림2]처럼 별도의 툴을 실행시켜야만 했다.
 
[그림2] 이전 NUnit의 UI