2008년 9월 12일 금요일

JUnit 3.8에서 JUnit 4, TestNG 활용으로

JUnit 3.8에서 JUnit 4, TestNG 활용으로
JUnit 3.8을 활용한 방식의 코드를 JUnit 4를 활용하도록 바꾸기.JUnit 4는 JDK 5의 애노테이션(Annotation)을 기반으로 유연한 테스트를 할 수 있게 개선되었다.예제는 Agile Java(TM): Crafting Code with Test-Driven Development (Robert C. Martin Series)의 코드를 기준으로 한다. Agile Java의 한글 번역서의 제목은 '자바 프로그래밍'인데, 서적의 특색을 제목에서 제거해버린 점이 아쉽다.
'자바 프로그래밍' 책 정보 보기

자바 프로그래밍Jeff Langr 지음, 권오근 옮김/교학사(컴퓨터)JUnit 버전을 바꿀 때 가장 먼저 고려할 것은 JUnit 라이브러리 jar 파일을 바꿔야 한다는 점이다. 이클립스에 포함된 jar를 기준으로 하면 JUnit 3.8은 junit.jar, JUnit 4는 junit3.8.1로 작명되어 있다. 이클립스를 사용하지 않는다면 http://junit.org에서 올바른 jar를 다운 받아서 클래스패스에 추가하면 된다. 이클립스를 사용하는 경우라면 다양한 방법으로 라이브러리를 추가할 수 있다. TestNG 사용의 경우라면 http://testng.org에서 jar를 다운 받거나 이클립스 플러그인 설치를 하면 된다.1. 테스트 케이스(Test Case)의 생성
public class StudentTest extends junit.framework.TestCase {}위와 같이 Junit 3.8의 경우는 명시적으로 JUnit이 제공하는 TestCase를 상속해야 한다. JUnit이 사실상 표준화 되어 있는 의존성이 크게 문제되는 경우는 아니라고 할지라도 강한 의존성이 생긴다.
public class StudentTest {}JUnit4에서는 이 시점까지는 별달리 해줄 것은 없다. 엄밀하게 보면 일단 앞서의 코드는 클래스패스에 junit.jar가 없다면 컴파일이 불가능하지만, 후자는 아무런 문제가 생기지 않는다. 하지만, 언어적 개선을 논하려는 상황이 아니라면 실용적으로는 테스트 코드에서 이러한 이점이 대단한 것이라고 보기는 힘들다. TestNG의 경우도 JUnit4와 동일하다.2. 테스트 케이스(Test Case)의 생성
public class StudentTest extends junit.framework.TestCase { public void testCreate() { }}자바 프로그래밍에 따르면 JUnit 3.8을 기준으로 테스트 메소드가 가져야 할 제약은 네 가지가 있다:
public 접근 지정자가 필요하고
반환 값은 void
인자도 없고
메소드 이름은 반드시 소문자 'test'로 시작해야 한다.
확인해보면 모두 AssertionFailedError가 발생하는데, 첫번째 제약을 위반하면 Test method isn't public 메시지를 출력하고, 나머지 경우는 No tests found라는 메시지를 출력한다.
public class StudentTest {@Testpublic void testCreate() {}}
JUnit4의 경우는 마지막 제약이 @Test 애노테이션을 붙여줘야 하는 것으로 바뀐다.@Test를 붙이지 않으면 테스트 메소드로 인식하지 못해서 실행이 불가능 하고, 전자의 세 가지 제약을 위반하면 모두 java.lang.Exception이 발생한다. 출력되는 메시지는 순서대로 Method testCreate should be public, Method testCreate should be void, Method testCreate should have no parameters 이다.test라는 일종의 접두어(prefix)를 붙이지 않아도 되는 것이 장점이 될지는 어떻게 쓰느냐의 문제다. 오히려 Junit 3.8의 작명 제약이 좋은 작명 관습을 유도할 수도 있기 때문이다. 만일 테스트 문서화가 요구되는 조직에서는 아래와 같이 한글을 쓸 수도 있다는 점은 큰 도움이 될 수도 있다.
public class StudentTest {@Testpublic void 객체생성() {}}
한편, 테스트 하려는 행위/기능/메소드 등의 이름을 그대로 사용하는 것도 가독성을 높여줄 수 있다.
public class StudentTest {@Testpublic void creation() {}}
TestNG의 경우도 JUnit4와 동일한 코드로 작성된다. 다만 @Test 애노테이션이 다른 네임스페이스를 갖는다.
import org.junit.Test; // junit4import org.testng.annotations.Test; // testng
그러나, TestNG는 JUnit의 제약에 대해 좀 더 유연하다. 하나씩 살펴보자. public 제약에 대해서는 예외를 발생시키지 않지만, 테스트 메소드로 인식하지 않는다. void 반환은 없다. 인자를 받을 수 있으나, 인자를 제공하지 않고 실행하면 TestNGException 예외가 발생한다. 어떤 면으로는 상당한 유연성 제공이지만, 다른 측면으로 보면 자칫 Separation of concerns를 위반하기 쉽게 할 여지로 볼 수도 있다.
이클립스 단축키
JUnit: Alt+Shift+X, TTestNG: Alt+Shift+X, N (패키지 익스플로러에서는 작동하나, 편집기에서는 먹통)3. assertion 메소드 작성
public void testCreate() {
Student student = new Student("Jane Doe");String studentName = student.getName();
assertEquals("Jane Doe", studentName);
}위와 같은 assertion 메소드를 활용하는 코드는 JUnit 모두 동일하다. 다만, 3.8의 경우는 상속한 TestCase에 정의된 메소드를 사용하는 것이고, JUnit4의 경우에는 assertEquals 메소드를 정적으로 import하여 사용하는 것이다. 따라서, 아래와 같은 import 문이 필요하다.
import static org.junit.Assert.assertEquals; // JUnit 4import static org.testng.Assert.assertEquals; // TestNGTestNG의 경우도 거의 유사하다. 다만, 이클립스 3.2의 JUnit4 지원이 TestNG 플러그인 지원에 비해 우수하다. (Organize import/Quick Fix/Run as/TestCase 생성 마법사 등)JUnit4와 TestNG를 비교하는 글은 여럿 존재한다.
JUnit V TestNG 비교 글...
JUnit V TestNG: Managing external dependencies
TestNG가 JUnit4에 비해 갖는 장점을 다룬 글이다.
여러 모로 봤을 때 기능적으로는 확실히 TestNG가 우위에 있다.
학습 비용도 워낙에 낮다.
그런데 걸리는 점은
1. JUnit은 이클립스에 기본 내장되어 있다.
- TestNG는 별도로 플러그인을 깔아야 한다. 2,3 분 정도 걸리는 간단한 일이지만...
2. Spring의 Test 유틸 클래스는 JUnit 3.x를 기준으로 구동한다.
- Spring 유틸리티를 TestNG 기반으로 변경해야 한다.
사실, 그렇게까지 해가면서 TestNG를 써야 할 이유를 찾기가 쉽지 않다.
다른 관련글:
관련글:
JUnit 4.0
JUnit 4 overview
(추가)
대용량 사이트에선 TestNG가 더 적합하다는 견해의 아티클:
compared TestNG and JUnit 4그러나, 꼭 둘을 대립적으로만 볼 필요는 없다. '블루오션'과 같은 책들을 보면서 태도를 바꾸게 되었는데..
블루오션 정보 보기

블루 오션 전략김위찬 외 지음, 강혜구 옮김/교보문고둘을 섞어서 사용할 수 있는가? 그렇다. 다만, 일관성 유지를 위해서 예전에 IDE 하드 디스크를 사용할 때 Mater/Slave와 같이 주가 되는 것을 정해주는 것이 좋다.
@org.testng.annotations.Testpublic void Create() {
Student student = new Student("Jane Doe");String studentName = student.getName();
org.junit.Assert.assertEquals("Jane Doe", studentName);
}위의 코드는 전혀 문제 없이 수행된다. 마찬가지로 아래 코드도 그렇다.
@org.junit.Testpublic void testCreate() {
Student student = new Student("Jane Doe");String studentName = student.getName();
org.testng.Assert.assertEquals("Jane Doe", studentName);
}물론, 위의 코드는 단순한 실험에 지나지 않고, 실효성이 있는지는 더 살펴볼 일이다.4. TestSuite 작성
public class AllTests { public static junit.framework.TestSuite suite() { junit.framework.TestSuite suite = new junit.framework.TestSuite(); suite.addTestSuite(StudentTest.class); suite.addTestSuite(CourseSessionTest.class); return suite; }}테스트 케이스를 스위트에 추가하는 방법이 자바 코드에서 애노테이션으로 바뀐 것이 가장 큰 특징이다.
@RunWith(Suite.class)@SuiteClasses({StudentTest.class, CourseSessionTest.class})public class AllTests {}
5. setUp 메소드 작성
public void setUp() { session = new CourseSession("ENGL", "101");}JUnit 3.8 에서는 역시 TestCase의 메소드를 상속 받는다.
@Beforepublic void setUp() {session = new CourseSession("ENGL", "101");}tearDown의 경우는 예상할 수 있겠지만 @After 애노테이션을 사용한다.setUp과 teardown의 진정한 개선점은 오버라이딩 대신에 애노테이션을 썼다는 점 보다는복수의 setUp/teardown이 가능해진 것이다.An early look at JUnit 4의 예제 코드를 빌려 오면
@Beforeprotected void findTestDataDirectory() {inputDir = new File("data");inputDir = new File(inputDir, "xslt");inputDir = new File(inputDir, "input");}
@Beforeprotected void redirectStderr() {System.setErr(new PrintStream(new ByteArrayOutputStream()));}자원 소모가 많은 작업을 위한 @BeforeClass/@AfterClass 등도 추가되었다.그 외에 몇 가지 JUnit 3.8 보다 JUnit4에서 개선된 점을 덧붙여본다.6. 예외 테스트의 개선 (예제 코드 출처: An early look at JUnit 4)
// JUnit 3.8 public void testDivisionByZero() { try { int n = 2 / 0; fail("Divided by zero!"); } catch (ArithmeticException success) { assertNotNull(success.getMessage()); }}// JUnit 4@Test(expected=ArithmeticException.class) public void divideByZero() { int n = 2 / 0;}TestNG와 거의 유사한 방식이다. try - catch 문은 사용할 필요가 없어 매우 간결하다.7. 수행 시간 측정 테스트
@Test(timeout = 2000)public void remoteBaseRelativeResolutionWithDirectory() throws IOException, ParsingException {builder.build("http://www.ibiblio.org/xml");}역시, TestNG를 이용하면 예전부터 사용할 수 있었던 방법이다. JUnit 3.8 시절에 이런 류의 테스트를 위해 JUnitPerf가 등장했다.8. 배열을 비교하는 asserttEquals 메소드 추가public static void assertEquals(Object[] expected, Object[] actual)기타@Ignore를 사용하면 특정 테스트를 실행 없이 넘길(skip) 수 있다.

댓글 없음:

댓글 쓰기