JUnit로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

JUnit로 검색한 결과
등록일:2008-03-14 14:11:50
작성자:
제목:JUnit 4.4 사용후기와 팁


JUnit 4.4 사용후기와 팁


  • 유상민(동영상 플랫폼본부 동영상 개발팀)

초록(Abstract)

3.8.1 이후 오랜기간 변경되지 않은 JUnit은 4.x대에 이르러 기존과 비교해서 빠른 변화를 보이고 있다. 이 문서에서는 JUnit 4.4에 추가 사항과 팁을 기록한다.

서론 #

JUnit[http://http://www.JUnit.org/ JUnit Web site]은 Test Driven Development [1](이하 TDD)의 개념을 정립한 Kent Beck이 직접 작성한 Java Unit Test Frameworks 이다.. Kent Beck은 Test Drivent Development By Example [http://www.amazon.com/Test-Driven-Development-Addison-Wesley-Signature/dp/0321146530 Test Driven Development By Example](이 하 TDDBE) 명서로 TDD의 개념을 잘 설명하였다. 이 문서는 TDD에 대한 설명은 TDDBE 같은 좋은책에 맡긴다. 이 문서에서는 JUnit 3.8 버전과 명확한 비교와 더불어 JUnit 4.4 에서 추가된 내용을 서술한다.

  • 모든 소스는 Copy & Paste 만으로 실행할수 있다.

준비 #

기본적인 시작은 Eclipse에 들어 있는 JUnitJUnit 4.1 이므로 http://JUnit.org 에서 JUnit 4.4 이상의 버전을 다운로드 받아서 classpath 상에 위치 시키고 Eclipse에서 자동 추가된 JUnit library는 제거한다.

JUnit 3와 JUnit 4.4 의 비교와 예제 #

JUnit 3 vs JUnit 4 #


3.x 4.4
test 작성 TestCase 를 상속받은 객체의 test로 시작하는 함수 @Test 이 붙은 함수
test 실행 Console 용, Swing 용 러너 제공 대부분 IDE에 통합 Console 용 러너 제공, Swing 러너 삭제
(이제 거의 모든 Java IDE에서 JUnit GUI Runner제공)
실행순서 setUp
testXXX
tearDown
@BeforeClass(반드시 static method)
@Before
@Test
@After
@AfterClass(반드시 static method)
assert TestCase 에 정의된 assert 문들 사용
(내부적으로 assert문은 정적 선언되어 있다.)
Assert 에 정의된 정적 assert 문들 사용,
assertThat 이 추가되었다.
기타 test대상 메소드의 객체가 TestCase를 상속받아야함 x

실행 예제 #

JUnit 3.8을 알고 있는 사람들이 좀더 자세한 내용을 원하면 [http]Get Acquainted with the New Advanced Features of JUnit 4 문서가 도움이 될 것이다.

아래의 예제는 서로 다른 버전이지만, 모두다 JUnit 4.4 에서 잘 돌아간다.

  • JUnit 3.8
import static java.util.Arrays.asList;

import JUnit.framework.TestCase;

public class SimpleTest extends TestCase {
private static int number;
private static Integer staticNumber;

public void setUp() {
number = 10;
}

public void testSimple() {
assertEquals(10, number);
assertEquals(
asList(new Integer[]{1,2,10}),
asList(new Integer[]{1,2,number}));

assertTrue(10 == number);

assertTrue(
asList(new String[]{"1"}).contains("1"));
assertTrue("123".contains("1"));
assertNull(staticNumber);
}

public void testDivideByZero() {
try {
assertEquals(0, number / 0);
} catch (ArithmeticException e) {
return;
}
fail();
}
// 실행 방지를 위해 _ 첨가
public void _testMultiply() {
assertEquals(100, number);
}

public void tearDown() {
number=0;
}
}

  • JUnit 4.4
import static org.hamcrest.CoreMatchers.*;
import static org.JUnit.Assert.*;
import static org.JUnit.matchers.JUnitMatchers.*;
import org.JUnit.*;

import static java.util.Arrays.asList;

public class SimpleTest {
private int number;
private static Integer staticNumber;

@BeforeClass
public static void beforeClass() {
staticNumber = 10;
}

@Before
public void setUpNumber() {
number = 10;
}

@Test
public void simple() {
assertEquals(10, number);
assertArrayEquals(
new Integer[]{1,2,10},
new Integer[]{1,2,number});

assertTrue(10 == number);
assertTrue(
asList(new String[]{"1"}).contains("1"));
assertTrue("123".contains("1"));
assertNotNull(staticNumber);


assertThat(number,is(10));
assertThat(
asList(new String[]{"1"}),hasItem("1"));

assertThat("123",containsString("1"));
assertThat(staticNumber,is(notNullValue()));
}

@Test(expected = ArithmeticException.class)
public void divideByZero() {
assertEquals(0, number / 0);
}

@Ignore("not ready yet")
@Test
public void multiply() {
assertEquals(100, number);
}
@After
public void clearNumber() {
number=0;
}
@AfterClass
public static void afterClass() {
staticNumber = null;
}
}


JUnit 4.4 에서 추가된 요소 #

JUnit 4.4는 초기 4.1에 비해 좀더 명확한 형정의와, 버그 수정 그리고 새로운 인자로 두가지 라이브러리 능력이 일부 포함되었다. [http://JUnit.sourceforge.net/doc/ReleaseNotes4.4.html Summary of Changes in version 4.4]

assertThat - Hamcrest - library of matchers for building test expressions #

2년 전에 Joe Walnes 는 JMock 1에 제약을 표현하기 위해 새로운 assertion 방법을 추가하였는데[http://joe.truemesh.com/blog/000511.html Joe Walnes의 블로그 Flexible JUnit assertions with assertThat()], 이는 읽기 쉬워서[http://weblogs.java.net/blog/tomwhite/archive/2006/05/literate_progra_1.html Literate Programming with jMock] 사람들에게 환영 받았다. 근본적으로, JUnit에서 제공되는 assertEquals 계열의 비교 구문과 기능은 다르지 않지만, 코드 자체가 읽기 편하며, assert 작성시 작성자의 의도를 더 정교하게 표현할수 있다. 이는 실패시 나오는 메세지가 읽기 매우 편하다는 것을 의미한다. 대략의 문법은 다음과 같다.

assertThat([값], [matcher 문법])

이런 jMock의 제약(assert) 관련 Matcher들은 Hamcrest[http://code.google.com/p/hamcrest/wiki/Tutorial Hamcrest - library of matchers for building test expressions] 로 따로 라이브러리가 되고 JUnit 4.4에 포함되었다.

String actual = "A";
assertEquals("A", actual);
이 형태의 코드는 다음과 같이 표현될 수 있다.

String actual = "A";
assertThat(actual , is("A"));
//That actual is "A"

이런 사용은 코드의 가독성 뿐만이 아니라, 에러코드의 가독성 까지 높일수 있다. 통상 코드 작성시 하게되는 사고는 영어식 어순의 사고인데, 이는 부드럽게 assert를 읽을수 있도록 한다.

다음은 assertThat으로 assert 작성자의 의도를 표현했을때 실패 메세지가 얼마나 명확히 표현되는지 보이는 예제이다.
import static org.hamcrest.CoreMatchers.*;
import static org.JUnit.Assert.assertThat;
import static org.JUnit.Assert.assertTrue;
import static org.JUnit.matchers.JUnitMatchers.containsString;
public class SimpleTest {
String responseString = "color col";
@Test
public void usingAssertTrue() {
assertTrue(responseString.contains("color") && responseString.contains("colour"));
}
@Test
public void usingAssertThat() {
assertThat(responseString, is(allOf(containsString("color"), containsString("colour"))));
// That responseString is a containning string "color" and a containning string "colour".
}
}

TestCase.assertTrue 의 결과 Assert.assertThat 의 결과
http://dna.daum.net/wiki/imgs/custom/<font style='background-color: rgb(51, 51, 51);' color='#ffff00' size='4'>JUnit</font>_reesult_assertTrue.png http://dna.daum.net/wiki/imgs/custom/<font style='background-color: rgb(51, 51, 51);' color='#ffff00' size='4'>JUnit</font>_reesult_assertThat.png
JUnit 4.4 에 포함된 Matcher들을 손쉽게 사용하기 위해서, JUnit 4.4에서는 CoreMatcher 와 JUnitMater 속에 라이브러리 상에서 제공하는 대부분의 Matcher들을 static 형태로 포함시켜 두었다.

import static org.hamcrest.CoreMatchers.*;
import static org.JUnit.matchers.JUnitMatchers.*;

Assumption, Theory Proper library #


JUnit 4.4에서 Assume과 @Theory 를 통한 Proper library [http://popper.tigris.org/ Proper Library] 의 능력들이 추가되었다.

Assume 은 기존에 코드 상에서 특정 조건을 만족시키면 실행시키고 싶은 (예~ A서버가 살아 있으면 실행시키고 싶다. ) 가정들을 프레임웍에서 지원하게 되었다.

만약 EXAMPLE_PATH이 존재하면 테스트를 수행한다고 가정하면 기존의 코드는 이런식이다.

public void test1(){
if(new File(EXAMPLE_PATH).exists() == false)
return;
// ... 테스트 코드
}

하지만 JUnit 4.4 에서 Assume 은 다음의 사용을 가능하게 만든다.

@Before
public void before(){
Assume.assumeThat(new File(EXAMPLE_PATH).exists(), is(true));
}
@Test
public void test1(){
FileReader fr = new FileReader(EXAMPLE_PATH);
// ... 테스트 코드
}

내부적으로 Assume.assumeThat은 org.JUnit.Assume.AssumptionViolatedException 예외를 던지며, 이는 아무 메세지 없고 실패, 성공 내용이 없는 예외이다. 현재 Eclipse나 기타 IDE에 addon된 JUnit들은 @Test내에서 이를 쓰면 실패로 취급하며, @Before에서 사용하면 정상적인 동작을 한다.

Assume 은 @Theory 와 더불어, 여러 가정을 만들어서 여러 Test값을 컨트롤 할수 있는 능력을 가질수 있는데, 이는 JUnit 4.4에 포함되었지만 org.JUnit.experimental 에 포함되어 있으므로, 차후 4.5 이후에 사용법이 명확해 지면 더 기술하도록 하겠다. Release Note[http://JUnit.sourceforge.net/doc/ReleaseNotes4.4.html JUnit ReleaseNote 4.4]의 예제를 기반으로 장난감 수준의 코드를 작성해 보았다.

import static org.hamcrest.CoreMatchers.*;
import static org.JUnit.Assert.assertThat;
import static org.JUnit.Assume.assumeThat;
import static org.JUnit.matchers.StringContains.containsString;
import org.JUnit.experimental.theories.DataPoint;
import org.JUnit.experimental.theories.Theories;
import org.JUnit.experimental.theories.Theory;
import org.JUnit.experimental.theories.suppliers.TestedOn;
import org.JUnit.runner.RunWith;
@RunWith(Theories.class)
public class TheoryExample {
public class User {
private String username;
public User(String username) {
this.username = username;
}
public String getUsername(){
return this.username;
}
}
@DataPoint public static String GOOD_USERNAME = "optimus";
@DataPoint public static String USERNAME_WITH_SLASH = "optimus/prime";
@Theory
public void filenameIncludesUsername(String username) {
assumeThat(username, not(containsString("/")));
assertThat(new User(username).getUsername(), containsString(username));
}
@SuppressWarnings("unchecked")
@Theory
public void multiplyIsInverseOfDivideWithInlineDataPoints(
@TestedOn(ints = { 0, 5, 10 }) int amount,
@TestedOn(ints = { 0, 1, 2 }) int m) {
assumeThat(m, not(0));
assertThat(amount*m/m, is(amount));
}
}

JUnit#

JUnit 4 에서는 TestCase를 만들기는 쉽다. 그런데 TestSuite 어느 문서에 나온거야? #

JUnit 3.8 에서는 TestSuite 상속받은 AllTest에 TestCase를 상속 받은 클래스들을 추가하는 형태로 묶음을 만들었다.

public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("Test for default package");
//$JUnit-BEGIN$
suite.addTestSuite(SimpleTest.class);
//$JUnit-END$
return suite;
}
}

JUnit 4에서는 @RunWith 를 이용해서 여러 테스트를 한꺼번에 실행시킬수 있다. [http://www.JUnit.org/JUnit/javadoc/4.3/org/JUnit/runner/RunWith.html @RunWith 에 대한 JavaDoc]

package test;
import org.JUnit.runner.RunWith;
import org.JUnit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses( { SimpleTest.class,SimpleTest2.class})
public class AllTests {
}

재미있게도, JUnit 4가 나온지 꽤 지났지만, 이 방법은 JUnit FAQ[http://JUnit.sourceforge.net/doc/faq/faq.htm#running_15 How do I organize all test classes in a TestSuite automatically and not use or manage a TestSuite explicitly?]에 기술되어 있지 않으며 @RunWith의 [http]JavaDoc에 만 그 사용법이 적혀 있다. FAQ에서는 Ant를 이용한 자동화나, JUnit을 도와주는 라이브러리는 이용한 자동화로 실행하기를 권장한다. TestCase가 많아질수록 위와 같이 명시적으로 Suite를 직접 만들어 쓰면, 테스트 실행을 누락할 수 있는 위험이 있다.

static import가 귀찮다. #

Eclipse에 범용적으로 쓸수있는 유용한 방법이다.

JUnit 4.x 의 @Test 에서 유효성 검사(Assert)를 하기 위해서는 Assert.assert***를 static import 하는 필수이다.

import static org.hamcrest.CoreMatchers.is;
import static org.JUnit.Assert.assertThat;
...
assertThat("A", is("A");
...

그런데, static import의 불편한 점은 문제는 Eclipse에서 이들을 사용할때 추천(Intellisense)를 해주지 않는 다는 불편함이 있다.

Eclipse 3.3에 추가된 Ctrl+3으로 favorite 을 검색해보면 다음의 설정화면을 만날수 있다. 이곳에서 추천(intellisense)시 검색에 포함할 static import 를 지정시킬수 있다.

http://dna.daum.net/wiki/imgs/custom/Preferences-favorite1.png

여기에서 주의할 점은, JUnit 4.4는 기존 버전과 호환성을 위해서 org.framework.Assert 포함하고 있으므로 반드시 org.JUnit.Assert를 선택한다. 이렇게 세팅해 놓으면 특별한 import 없이도 다음과 같이 사용할수 있다.

http://dna.daum.net/wiki/imgs/custom/java-editor0.png

Eclipse에서 곧 바로 JavaDoc을 보고 싶다. #

Eclipse에 범용적으로 쓸수있는 유용한 방법이다.

Eclipse에서 F2키는 해당 API의 JavaDoc 을 Eclipse 가 랜더링해서 보여준다. 그런데, Shift+F2를 하면, 지정된 JavaDoc을 Eclipse가 최우선하는 웹브라우저에서 보여준다. JDK나 JRE로 세팅된 sun사의 공식 JavaDoc에 미리 연결되어 있다. 지금 당장 다음을 소스를 작성한다음

String example = "example";

String 위치에서 Shift+F2 를 누르면, 웹브라우저에서 Sun사에 JavaDoc을 구경할수 있다.

이 기능을 모른다면, 우리는 JavaDoc 찾기 위해 그 위치를 다음신이나 구글신에게 의존할 수 밖에 없다. 그런데, 대부분의 오픈소스 라이브러리 특히 apache commons[http://commons.apache.org/ Apache Commons] 같은 JDK 수준으로 쓰이는 라이브러리들은 프로젝트 홈페이지에서 JavaDoc을 제공한다. Eclipe에서는 library를 classpath에 세팅하는 과정에서 해당 라이브러리와 JavaDoc을 연결하는 옵션을 제공한다.

http://dna.daum.net/wiki/imgs/custom/properties_relate_javadoc0.png

http://JUnit.sourceforge.net/javadoc_40/index.html

이렇게 세팅해두면, Shift+F2 를 이용해서 해당 라이브러리의 JavaDoc을 웹상을 뒤질필요 없이 곧 바로 볼수 있다.

만약, 웹상에 JavaDoc을 제공하지 않고, 따로 JavaDoc을 제공한다면, 이를 다운 받아 zip형태로 묶은 다음 프로젝트에 일정 공간에 포함시켜 그림의 Javadoc in archive로 포함시키면, Eclipse를 스스로 서버를 띄워서 제공한다. 좌측 옵션에 볼수 있듯이 Java Source Attachment를 통해서 소스도 링크 시켜 둘수 있다. (Apache Commons를 사용하면서 특히나 유용하였다.)

위의 @RunWith 를 이용한 TestSuite 작성 방법도 이 세팅후에 JavaDoc을 둘러 보는 과정에서 알 수 있었다.

Eclipse에서는 이 외에도 수많은 유용한 세팅들이 있는데, 많은 경우 사용하지 않는 것 같다. 차후 기회가 마련되면 다시 언급해보겠다.

결론 #

JUnit 4.4에서 새롭게 도입된 assertThat 을 써보면서, 가독성의 증가를 느끼고 있다. 더불어, 불만이었던 assert의 표현력에 증가를 체감할 수 있었다.

아직 JUnit에는 jMock처럼 풍부한 Matcher들을 모두 포함 되지는 않았다. 차후 버전에서 합의된 더 많은 수의 Matcher의 지원이 기대된다. (아직도 JUnit 메일리 리스트[http://tech.groups.yahoo.com/group/JUnit/ Yahoo Groups JUnit] 활기를 띄는 점이 놀랍다.) 그리고 @Theory 를 통한 더 손쉬운 테스트를 기대한다.