SSISO Community검색 |
|
SSISO Community메뉴 |
|
SSISO Community카페 |
|
블로그 카테고리 |
|
|
JUnit로 검색한 결과 |
|
등록일:2008-03-14 14:11:50 작성자: 제목:JUnit 4.4 사용후기와 팁 |
|
JUnit 4.4 사용후기와 팁
3.8.1 이후 오랜기간 변경되지 않은 JUnit은 4.x대에 이르러 기존과 비교해서 빠른 변화를 보이고 있다. 이 문서에서는 JUnit 4.4에 추가 사항과 팁을 기록한다.
서론 #
JUnit은 Test Driven Development (이하 TDD)의 개념을 정립한 Kent Beck이 직접 작성한 Java Unit Test Frameworks 이다.. Kent Beck은 Test Drivent Development By Example (이
하 TDDBE) 명서로 TDD의 개념을 잘 설명하였다. 이 문서는 TDD에 대한 설명은 TDDBE 같은 좋은책에 맡긴다. 이
문서에서는 JUnit 3.8 버전과 명확한 비교와 더불어 JUnit 4.4 에서 추가된 내용을 서술한다.
- 모든 소스는 Copy & Paste 만으로 실행할수 있다.
준비 #
기본적인 시작은 Eclipse에 들어 있는 JUnit이 JUnit 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 4.4 에서 잘 돌아간다.
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; } }
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에 비해 좀더 명확한 형정의와, 버그 수정 그리고 새로운 인자로 두가지 라이브러리 능력이 일부 포함되었다.
assertThat - Hamcrest - library of matchers for building test expressions #
2년 전에 Joe Walnes 는 JMock 1에 제약을 표현하기 위해 새로운 assertion 방법을 추가하였는데, 이는 읽기 쉬워서
사람들에게 환영 받았다. 근본적으로, JUnit에서 제공되는 assertEquals 계열의 비교 구문과 기능은 다르지 않지만,
코드 자체가 읽기 편하며, assert 작성시 작성자의 의도를 더 정교하게 표현할수 있다. 이는 실패시 나오는 메세지가 읽기
매우 편하다는 것을 의미한다. 대략의 문법은 다음과 같다.
assertThat([값], [matcher 문법])
이런 jMock의 제약(assert) 관련 Matcher들은 Hamcrest 로 따로 라이브러리가 되고 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 의 결과 |
| |
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 의 능력들이 추가되었다.
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의 예제를 기반으로 장난감 수준의 코드를 작성해 보았다.
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 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 를 이용해서 여러 테스트를 한꺼번에 실행시킬수 있다.
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에 기술되어 있지 않으며 @RunWith의 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 를 지정시킬수 있다.
여기에서 주의할 점은, JUnit 4.4는 기존 버전과 호환성을 위해서 org.framework.Assert 포함하고 있으므로 반드시 org.JUnit.Assert를 선택한다. 이렇게 세팅해 놓으면 특별한 import 없이도 다음과 같이 사용할수 있다.
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 같은 JDK 수준으로 쓰이는 라이브러리들은 프로젝트 홈페이지에서 JavaDoc을 제공한다. Eclipe에서는 library를 classpath에 세팅하는 과정에서 해당 라이브러리와 JavaDoc을 연결하는 옵션을 제공한다.
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 메일리 리스트 활기를 띄는 점이 놀랍다.) 그리고 @Theory 를 통한 더 손쉬운 테스트를 기대한다.
|
|
|
|
|
|