 |
|
난이도 : 초급 Filippo Diotalevi, IT 전문가, IBM Italy
2005 년 1 월 06 일 JUnit 프레임웍은 현제 자바의 단위 테스트를 위한 솔루션이다. 이 프레임웍은 자바 개발자들에게 테스트 지향 개발이라는 개념을 소개했고, 단위 테스트를 효과적으로 작성하는 방법을 가르쳤다는 점에서 높이 평가 받을만 하다. 하지만 JUnit은 지난 몇 년 동안 그 한계에 다다랐다. 따라서 오늘날의 복잡한 환경에 적합한 테스트를 작성하는 것은 점점 더 어려운 일이 되어가고 있다. JUnit은 다른 보완 테스트 프레임웍들과 통합되어야 한다. 이 글에서 Filippo Diotalevi가 자바 애플리케이션 테스트를 위한 새로운 프레임웍인 TestNG를 소개한다. TestNG는 강력하고, 혁신적이며, 확장성 있고, 유연할 뿐만 아니라 JDK 5.0의 획기적인 신 기능인 Java Annotations의 재미있는 애플리케이션을 나타내기도 한다.
현대의 모든 소프트웨어 패키지의 구현 단계에서 테스팅은 중심적 역할을 한다. 먼저 코딩 하고 테스트는 시간 날 때 아무때나(아니면, 전혀 하지 않던) 하던 시대는 지났다. 요즘, 대부분의 개발자들은 코딩과 테스팅이 동시에 이루어져 일찍 버그를 잡고 개발 프로세스 초기 단계에 주요 위험을 규명할 수 있는 이러한 소프트웨어 방식을 채택할 필요를 인식하고 있다.
여타의 테스팅 프레임웍과는 달리 JUnit을 통해 개발자들은 테스트(특히 단위 테스트)의 유용함을 이해하게 되었다. 보다 간단하고, 실용적이며, 정밀한 아키텍쳐를 이용하는 JUnit은 상당히 많은 개발자들에게 영향을 주었다.(참고자료) JUnit 사용자들은 단위 테스트에 대한 기본적인 규칙을 배웠다:
- 모든 코드는 테스트되어야 한다.
- 가능한 모든 때에, 코드는 (예를 들어, mock 객체 같은 기술을 사용하여)독립적으로 테스트되어야 한다.
- 소프트웨어는 테스트가 쉬워야 한다. 다시 말해서, 테스트를 염두해 두고 작성되어야 한다.
하지만 테스트에 대한 개발자의 자신감이 커지면서 JUnit의 단순함과 엄격함은 두 가지 상충 요소들로 나뉘었다. 한편에서는, JUnit의 단순함이 ‘소프트웨어 역시 단순해야 한다’라고 프로그래머들에게 상기시킬 필요가 있다고 강하게 믿는 사람이 생겼고(이를 KISS(keep it simple, stupid) 원리라고 한다), 다른 한편에는 JUnit이 극도로 단순해질 것을 모색하는 사람들로서 이들은 다른 테스팅 프레임웍 보다 앞선 기능, 더 나아진 유연성, 더 강한 힘을 원했다. JUnit의 몇 가지 특정 기능들은 후자의 사람들로부터 비난을 받았다:
- 자바는 단일 상속이기 때문에
TestCase 클래스를 확장할 필요성이 매우 제한적이다.
- 매개변수들을 JUnit의 테스트 메소드에 전달하는 것과
setUp() 과 tearDown() 메소드에 전달하는 것은 불가능하다.
- 실행 모델이 익숙하지 않다: 테스트클래스는 테스트 메소드가 실행될 때 마다 다시 인스턴스화된다.
- 복잡한 프로젝트에서 다른 테스트 슈트의 관리는 트릭을 많이 써야 한다.
 |
TestNG의 작성자
TestNG를 처음 만든 사람은 Cedric Beust이다. 자바 프로그래밍 분야에서는 잘 알려진 인물로서 EJB 3 전문가 그룹의 멤버이자, 다른 유명한 오픈 소스 프로젝트들(EJBGen과 Doclipse)의 작성자이기도 하다. TestNG는 Apache Software License로 배포되고 웹 사이트에서 다운로드 할 수 있다. (참고자료)
|
|
이 글에서, TestNG라고 하는 새로운 테스팅 프레임웍을 사용하여 애플리케이션을 위한 단위 테스트를 작성하는 방법을 배우게 될 것이다. TestNG는 JUnit에서 영감을 받았고 후자의 단순함을 유지한다. 동시에 TestNG는 구식 프레임웍의 제한 대부분을 없애고 개발자들이 유연하고 강력한 테스트를 작성할 수 있도록 한다. Java Annotations(JDK 5.0에 도입 (참고자료)를 상당부분 참조하여 테스트를 정의했기 때문에 실제 제품 환경에서 자바의 새로운 기능을 사용하는 방법도 알게 될 것이다.
코드에 대하여
TestNG의 사용법을 설명하기 위해서 Jakarta Common Lang 이라는 널리 사용되는 오픈 소스 라이브러리용 단위 테스트를 작성할 것이다. 여기에는 몇 가지 유용한 클래스들이 포함되어 있어 스트링, 넘버, 자바 객체들을 핸들 및 조작할 것이다. (참고자료)
TestNG는 두 개의 다른 패키지에서 사용할 수 있다: 하나는 JDK 5.0이 필요하고, 다른 하나는 1.4 버전과 호환되는 것이다. 이들은 약간 다른 문법을 사용하여 테스트를 정의한다: 전자는 JDK 5.0 어노테이션을 사용하는 반면, 후자는 오래된 Javadoc 스타일의 어노테이션을 사용한다. 이 글에서는 JDK 5.0 버전을 사용하기 때문에 어노테이션에 대한 기본적인 이해가 필요하다.(참고자료) 하지만 JDK 5.0이 테스트를 컴파일하고 실행하는 데에만 필요하다는 것을 알아야 한다. 선호하는 컴파일러로 애플리케이션 코드를 구현할 수도 있다. 사실 Jakarta 프로젝트의 웹 사이트에서 다운로드 할 수 있는 같은 JAR 파일을 사용하여 Jakarta Common Lang 라이브러리를 테스트하는 것이다. TestNG 웹 사이트에서 TestNG version 1.4에 대한 자세한 정보를 얻을 수 있다.
마지막으로 이 글 상단과 하단에 있는 Code 아이콘을 클릭하여 j-testng-sample.zip을 다운로드 한다. 여기에는 TestNG를 사용하여 Jakarta Commons Lang의 단위 테스트를 작성하는 방법을 설명하는 예제들이 포함되어 있다.
TestNG 시작하기
TestNG 테스트 클래스는 그저 오래된 자바 객체일 뿐이다; 특별한 클래스를 확장하거나 테스트 메소드를 위한 네이밍 규약을 사용할 필요가 없다: 이 프레임웍에 대한 신호로 @Test 어노테이션을 사용하면 된다. Listing 1은 유틸리티 클래스인 StringUtils에 사용할 수 있는 간단한 테스트를 보여주고 있다. StringUtils의 두 가지 메소드를 테스트 한다: isEmpty()는 공백을 위한 String을 검사하고 trim()은 String의 양 끝에서 제어 문자를 제거한다. 자바 명령어인 assert는 에러 조건을 검사하는데 사용된다.
Listing 1. StringUtils 클래스를 위한 테스트
package tests;
import com.beust.testng.annotations.*;
import org.apache.commons.lang.StringUtils;
public class StringUtilsTest
{
@Test
public void isEmpty()
{
assert StringUtils.isBlank(null);
assert StringUtils.isBlank("");
}
@Test
public void trim()
{
assert "foo".equals(StringUtils.trim(" foo "));
}
}
|
테스트를 실행하기 전에 특별한 XML 파일(일반적으로, testng.xml)을 사용하여 TestNG를 설정해야 한다. 이 파일의 문법은 매우 간단하다. (Listing 2) 이 파일은 StringUtilsTest클래스에 의해 만들어진 First test로 구성된 테스트 슈트인 My test suite를 정의하는 것으로 시작한다.
Listing 2. TestNG의 설정 파일
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
<test name="First test">
<classes>
<class name="tests.StringUtilsTest" />
</classes>
</test>
</suite>
|
이 샘플 testing.xml 파일이 그다지 유용하지 않은 것 같지만 (단 하나의 테스트 클래스만 있다면), 바로 이것이 테스트 슈트를 정의하기 위해 작성하는데 필요한 유일한 파일이다. JUnit을 기억하는가? 그 당시, 슈트의 정의는 아마도 여러 파일들에 걸쳐 퍼져있었다: JUnit의 TestSuite, 속성 파일, Ant 구현 파일. TestNG를 사용하면 필요한 모든 데이터들은 testng.xml 파일에 모아진다. TestSuite와 구현 파일이 필요하지 않다.
이 테스트를 실행하려면 javac로 클래스를 컴파일하고 다음 명령어로 TestNG를 호출한다:
java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml |
-ea 옵션은 JVM이 assertion을 핸들하도록(그리고 assertion이 실패했을 때 예외를 만들 것을) 명령한다; testng.jar와 commons-lang-2.0.jar는 이 예제를 실행하는데 필요한 라이브러리들이다. com.beust.testng.TestNG는 TestNG의 메인 클래스이다. java와 javac의 신비한 문법을 행복하게도 잊어버린 모든 게으른 개발자들은 유용한 Ant 태스크를 사용할 수 있다. Listing 3은 애플리케이션 샘플의 Ant 구현 파일이다. testng태스크의 정의는 com.beust.testng.TestNGAntTask클래스와 관련되어 있고 테스트 목적에 맞게 비교적 간단하다.
Listing 3. TestNG 태스크를 이용한 Ant 구현 파일
<project name="sample" default="test" basedir=".">
<!-- COMPILE TESTS-->
<path id="cpath">
<pathelement location="testng.jar"/>
<pathelement location="commons-lang-2.0.jar"/>
</path>
<target name="compile">
<echo message="compiling tests"/>
<mkdir dir="classes"/>
<javac debug="true"
source="1.5" classpathref="cpath"
srcdir="src" destdir="classes"/>
</target>
<!-- RUN TESTS-->
<taskdef name="testng"
classname="com.beust.testng.TestNGAntTask"
classpathref="cpath"/>
<path id="runpath">
<path refid="cpath"/>
<pathelement location="classes"/>
</path>
<target name="test" depends="compile">
<echo message="running tests"/>
<testng fork="yes" classpathref="runpath" outputDir="test-output">
<fileset dir="src" includes="testng.xml"/>
<jvmarg value="-ea" />
</testng>
</target>
</project>
|
모든 것이 정확히 수행되었다면 콘솔에서 테스트 결과를 볼 수 있을 것이다. TestNG는 test-output이라는 폴더에 훌륭한 HTML 리포트를 만든다. 이것은 현재 디렉토리에 자동으로 만들어진다. 이것을 열어 index.html을 로딩하면 그림 1과 같은 페이지를 보게된다.
그림 1. TestNG에서 만들어진 HTML 리포트
테스트 그룹 정의하기
TestNG의 또 다른 흥미로운 기능은 테스트 그룹을 정의하는 기능이다. 모든 테스트 메소드는 한 개 이상의 그룹들과 연합할 수 있다. 일단 이들 그룹들을 정의하면 특정 그룹의 테스트만 실행할 수 있다. 테스트 그룹에 테스트를 추가하려면 이 그룹을 @Test 어노테이션의 매개변수로 지정한다. 다음 문법을 사용한다:
@Test(groups = {"tests.string"})
|
이 경우, 주석이 달인 메소드는 tests.string 그룹에 속했음을 선언한다. 매개변수 groups 는 어레이기 때문에 다중 그룹들을 지정하면서 콤마로 이름들을 구별한다. 예를 들어, 샘플 애플리케이션에서 Stringnumbers, Booleans 별로 다른 테스트를 만들고 이들을 선택적으로 실행하면서 TestNG를 설정할 수 있다. (Listing 4)
Listing 4. 다양한 그룹별 설정 파일
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My suite">
<test name="Simple example">
<groups>
<run>
<include name="tests.string" />
<include name="tests.math" />
<exclude name="tests.boolean"/>
</run>
</groups>
<classes>
.... list classes here....
</classes>
</test>
</suite>
|
분명히, 다른 테스트 그룹들을 실행하면 HTML 리포트는 모든 테스트들을 하나의 리스트로 보여줄 것이고 각 그룹의 개별 리스트를 통해 문제의 원인들을 즉시 파악할 수 있다.
설정 메소드
TestNG를 사용하면 테스트 메소드 외의 것들을 지정할 수 있다; @Configuration어노테이션을 사용하여 클래스 안에 설정 메소드라고 하는 특정 메소드를 지정할 수 있다. 다음은 네 가지 유형의 설정 메소드들이다:
beforeTestClass 메소드 : 테스트 메소드가 실행되기 전, 클래스가 인스턴스로 된 후에 실행된다.
afterTestClass 메소드 : 클래스의 모든 테스트 메소드가 실행된 후 이 메소드가 실행된다.
beforeTestMethod 메소드 : 클래스의 모든 테스트 메소드가 실행되기 전에 이 메소드가 실행된다.
afterTestMethod 메소드 : 클래스의 모든 테스트 메소드들이 실행된 후 이 메소드가 실행된다.
그림 2는 테스트 클래스의 수명주기 이다.
그림 2. 테스트 클래스 수명주기
Listing 5는 설정 메소드의 예제이다. 그룹들을 사용한다면 설정 메소드는 그룹에 속해야 한다. 네 가지 유형의 설정 메소드가 상호 배타적이지 않기 때문에 동시에 이들 목록 중 한 개 이상의 목록에 속해있는 메소드들을 정의할 수 있다. (Listing 5의 aroundTestMethods() 메소드 참조)
Listing 5. Examples of 설정 메소드
@Configuration(beforeTestClass = true, groups = {"tests.workflow"})
public void setUp()
{
System.out.println("Initializing...");
}
@Configuration(afterTestMethod = true, beforeTestMethod = true, groups = {"tests.workflow"})
public void aroundTestMethods()
{
System.out.println("Around Test");
}
|
TestNG의 설정 메소드는 JUnit의 setUp()과 tearDown() 메소드에서 나아간 것이다. 목적은 테스트에 맞는 실행 콘텍스트를 만들고 테스트 케이스의 실행 후에 데이터를 재생하기 위함이다.
예외 검사
TestNG를 사용하여 매우 쉽고 간단히 예외 발생을 검사할 수 있다. JUnit을 사용해서도 가능하겠지만, TestNG의 @ExpectedExceptions 어노테이션을 사용하면 테스트 코드 작성이 매우 단순하고 쉽다. @ExpectedExceptions 어노테이션은 NumberFormatException의 발생을 지정하기 때문에 실패로 간주되어서는 안된다. 코드의 특정 라인에서 예외가 발생하는지 보려면 이 라인 바로 다음에 assert false 문장을 추가할 수 있다. 예정된 라인에서 특정 유형의 예외가 발생할 경우에만 테스트가 통과한다는 것을 의미한다.
Listing 6. TestNG의 예외 검사
public class NumberUtilsTest
{
@Test(groups = {"tests.math"})
@ExpectedExceptions(NumberFormatException.class)
public void test()
{
NumberUtils.createDouble("12.23.45");
assert false; //shouldn't be invoked
}
}
|
결론
이 글에서 TestNG를 소개했다. 여러분이 지금 당장이라도 단위 테스트를 작성할 수 있었으면 좋겠다. 하지만 이는 완벽한 레퍼런스 매뉴얼은 아니다. TestNG에는 이 외에도 많은 유용한 기능들이 있다:
- 테스트 메소드와 설정 메소드에 인자들을 전달할 수 있다. 어노테이션을 사용하거나 XML 설정 파일에서 이를 선언한다.
- "compatibility mode”를 사용하여 TestNG 밑에 좋은 JUnit 테스트를 실행할 수 있다.
- 테스트 그룹들 간 의존관계를 만들어 실행 순서를 결정한다.
TestNG 문서(참고자료)를 참조하기 바란다.
이 모든 기능들은 테스트 정의에 자바 어노테이션을 채택하는 것과 더불어 전체 테스팅 프로세스를 훨씬 간단하고 유연하게 한다. 테스트 작성시 준수해야 하는 규칙들도 적다. 이 모든 것을 떠나서 다양한 테스팅 전략을 선택할 수 있다.
TestNG는 분명 단위 테스트를 위한 탁월한 선택이고 다른 라이브러리들과 툴과도 통합이 간단하여 개발자들에게는 이보다 더 좋은 소식은 없다.
다운로드 하십시오 | 이름 | 크기 | 다운로드 방식 |
|---|
| j-testng-sample.zip | |
FTP |
참고자료
필자소개  | 
|  | Filippo Diotalevi는 IBM(이탈리아)의 IT 전문가이다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|