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

황제낙엽 2007.09.17 18:33 조회 수 : 369 추천:81

sitelink1  
sitelink2  
sitelink3 http://1 
extra_vars4 ko 
extra_vars5 http://tong.nate.com/weehokyu/34885954 
extra_vars6 sitelink1 
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의 한글 번역서의 제목은 '자바 프로그래밍'인데, 서적의 특색을 제목에서 제거해버린 점이 아쉽다.

'자바 프로그래밍' 책 정보 보기


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 {
@Test
public 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 {
@Test
public void 객체생성() {
}
}

한편, 테스트 하려는 행위/기능/메소드 등의 이름을 그대로 사용하는 것도 가독성을 높여줄 수 있다.

public class StudentTest {
@Test
public void creation() {
}
}

TestNG의 경우도 JUnit4와 동일한 코드로 작성된다. 다만 @Test 어노테이션이 다른 네임스페이스를 갖는다.

import org.junit.Test; // junit4

import org.testng.annotations.Test; // testng

그러나, TestNG는 JUnit의 제약에 대해 좀 더 유연하다. 하나씩 살펴보자. public 제약에 대해서는 예외를 발생시키지 않지만, 테스트 메소드로 인식하지 않는다. void 반환은 없다. 인자를 받을 수 있으나, 인자를 제공하지 않고 실행하면 TestNGException 예외가 발생한다. 어떤 면으로는 상당한 유연성 제공이지만, 다른 측면으로 보면 자칫 Separation of concerns를 위반하기 쉽게 할 여지로 볼 수도 있다.

이클립스 단축키


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 4
import static org.testng.Assert.assertEquals; // TestNG

TestNG의 경우도 거의 유사하다. 다만, 이클립스 3.2의 JUnit4 지원이 TestNG 플러그인 지원에 비해 우수하다. (Organize import/Quick Fix/Run as/TestCase 생성 마법사 등)

JUnit4와 TestNG를 비교하는 글은 여럿 존재한다.

JUnit V TestNG 비교 글...


그러나, 꼭 둘을 대립적으로만 볼 필요는 없다. '블루오션'과 같은 책들을 보면서 태도를 바꾸게 되었는데..

블루오션 정보 보기


둘을 섞어서 사용할 수 있는가? 그렇다. 다만, 일관성 유지를 위해서 예전에 IDE 하드 디스크를 사용할 때 Mater/Slave와 같이 주가 되는 것을 정해주는 것이 좋다.

@org.testng.annotations.Test
public void Create() {
Student student = new Student("Jane Doe");
String studentName = student.getName();
org.junit.Assert.assertEquals("Jane Doe", studentName);
}

위의 코드는 전혀 문제 없이 수행된다. 마찬가지로 아래 코드도 그렇다.

@org.junit.Test
public 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의 메소드를 상속 받는다.

@Before
public void setUp() {
session = new CourseSession("ENGL", "101");
}

tearDown의 경우는 예상할 수 있겠지만 @After 어노테이션을 사용한다.

setUp과 teardown의 진정한 개선점은 오버라이딩 대신에 어노테이션을 썼다는 점 보다는
복수의 setUp/teardown이 가능해진 것이다.

An early look at JUnit 4의 예제 코드를 빌려 오면
@Before
protected void findTestDataDirectory() {
inputDir = new File("data");
inputDir = new File(inputDir, "xslt");
inputDir = new File(inputDir, "input");
}
@Before
protected 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) 수 있다.

출처 : Tong - exospace님의 JAVA통

번호 제목 글쓴이 날짜 조회 수
57 셀 크기 조정 (자동 크기 조정) 황제낙엽 2011.05.03 7740
56 Cell 의 wrap 설정 (텍스트 개행) file 황제낙엽 2011.05.09 2966
55 POI HSSF, XSSF, SXSSF 성능 분석 file 황제낙엽 2013.11.05 1590
54 병합된 셀의 스타일( border) 설정하기 황제낙엽 2011.05.03 1564
53 Parsing and Processing Large XML Documents with Digester Rules (해석중) file 황제낙엽 2008.05.13 1478
52 POI 셀 스타일 설정을 위한 예제 소스 file 황제낙엽 2008.05.16 1379
51 엑셀(Excel)문서 처리 패키지 황제낙엽 2007.01.22 1334
50 POI-HSSF and POI-XSSF - Java API To Access Microsoft Excel Format Files 황제낙엽 2013.11.05 984
49 Comma Separated Values (CSV) - au.com.bytecode.opencsv file 황제낙엽 2007.01.23 626
48 Parsing, indexing, and searching XML with Digester and Lucene 황제낙엽 2008.05.07 429
47 POI HSSF 기능 가이드 -- 퀵·가이드 (한글) 황제낙엽 2008.05.16 373
» JUnit 3.8에서 JUnit 4, TestNG 활용으로 황제낙엽 2007.09.17 369
45 Junit 을 이용한 효율적인 단위 테스트 전략 황제낙엽 2007.01.30 317
44 Library & Properties 파일 file 황제낙엽 2011.12.23 313
43 XSSF Examples file 황제낙엽 2011.05.04 254
42 사용자 정의 Appender 정의하여 Log4j 확장하기 황제낙엽 2009.05.28 220
41 log4j-1.2.15.jar 와 log4j.properties 예제 file 황제낙엽 2017.08.04 187
40 접속 클라이언트의 아이피별로 로그 화일 기록하기 file 황제낙엽 2009.06.01 183
39 Comma Separated Values (CSV) - com.Ostermiller.util Java Utilities 황제낙엽 2007.01.23 177
38 Apache Log4j 2 Configuration 파일 설정 황제낙엽 2020.04.01 150