'2008/01'에 해당되는 글 5건
예제를 통한 TestNG 살펴보기 :: 2008/01/31 18:52
TestNG는 단위테스트를 비롯하여 기능테스트, 통합테스트, 인수테스트 등 모든 범주의 테스트를 지원하는 것을 추구한다. JUnit보다 더욱 유연하고 쉬운 테스트가 가능하며, 효율적인 테스트를 위한 많은 기능들을 제공하고 있다. 차세대 자바 테스팅 엔진으로서 손색이 없는 TestNG의 사용법을 예제를 통해 알아본다.
1. 기본 테스트 예제import org.testng.Assert;
import org.testng.annotations.Test;
public class SimpleTest {
private int x = 2;
private int y = 3;
@Test
public void testAddition() {
int z = x + y;
Assert.assertEquals(5, z);
}
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("Fast test");
}
@Test(groups = { "slow" })
public void aSlowTest() {
System.out.println("Slow test");
}
} |
- JUnit과는 다르게 테스트 메소드를 groups 파미미터 지정으로 특정 그룹으로 지정할 수 있다.
- 이렇게 지정된 그룹은 이후에 그룹별 실행이나 종속성을 갖도록 설정되어질 수도 있다.
- 아래는 테스트를 수행한 한 예로 testng.xml 파일을 이용한 것이다. 결과는 콘솔에 보여준다.
C:\TestingWorks\TestNG01>java org.testng.TestNG testng.xml
[Parser] Running:
D:\WORKS\TestingWorks\TestNG01\temp-testng-customsuite.xml
Slow test
Fast test
PASSED: aSlowTest
PASSED: testAddition
PASSED: aFastTest
===============================================
SimpleTest
Tests run: 3, Failures: 0, Skips: 0
===============================================
===============================================
TestNG01
Total tests run: 3, Failures: 0, Skips: 0
=============================================== |
- 테스트 수행은 다양하게 설정되어 질 수 있는데, 위 테스트의 testng.xml의 내용이다.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestNG01">
<test verbose="2" name="SimpleTest" annotations="JDK">
<classes>
<class name="testing.testng.SimpleTest"/>
</classes>
</test>
</suite> |
2. 테스트 메소드 실행 전/후에 처리작업 수행하기
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
public class BeforeAfterTest {
@BeforeSuite
public void beforeSuite() {
System.out.println("@BeforeSuite");
}
@AfterSuite
public void afterSuite() {
System.out.println("@AfterSuite");
}
@BeforeTest
public void beforeTest() {
System.out.println("@BeforeTest");
}
@AfterTest
public void afterTest() {
System.out.println("@AfterTest");
}
@BeforeClass
public void beforeClass() {
System.out.println("@BeforeClass");
}
@AfterClass
public void afterClass() {
System.out.println("@AfterClass");
}
@BeforeGroups(groups = { "fast" })
public void beforeGroups() {
System.out.println("@BeforeGroups: fast");
}
@AfterGroups(groups = { "fast" })
public void afterGroups() {
System.out.println("@AfterGroups: fast");
}
@BeforeMethod
public void beforeMethod() {
System.out.println("@BeforeMethod");
}
@AfterMethod
public void afterMethod() {
System.out.println("@AfterMethod");
}
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("Fast test");
}
@Test(groups = { "slow" })
public void aSlowTest() {
System.out.println("Slow test");
}
} |
- 실행 전 순서: @BeforeSuite -> @BeforeTest -> @BeforeClass -> @BeforeGroups -> @BeforeMethod
- 실행 후 순서: @AfterMethod -> @AfterGroups -> @AfterClass -> @AfterTest -> @AfterSuite
- 아래는 테스트를 수행시 아래와 유사한 결과를 볼 수 있다.
[Parser] Running:
C:\TestingWorks\TestNG01\temp-testng-customsuite.xml
@BeforeSuite
@BeforeTest
@BeforeClass
@BeforeMethod
Slow test
@AfterMethod
@BeforeGroups: fast
@BeforeMethod
Fast test
@AfterMethod
@AfterGroups: fast
@AfterClass
@AfterTest
PASSED: aSlowTest
PASSED: aFastTest
===============================================
testing.testng.study02.BeforeAfterTest
Tests run: 2, Failures: 0, Skips: 0
===============================================
@AfterSuite
===============================================
TestNG01
Total tests run: 2, Failures: 0, Skips: 0
=============================================== |
3. 테스트 그룹 지정을 통한 보다 유연한 테스트하기
import org.testng.annotations.Test;
@Test(groups = {"func-test"})
public class GroupTest {
@Test(groups = { "fast" })
public void aFastTest() {
System.out.println("Fast test");
}
@Test(groups = { "slow", "broken" })
public void aSlowTest() {
System.out.println("Slow test");
}
} |
- 테스트 설정파일 testng.xml을 통해 테스트 코드가 완성되지 않은 특정 그룹에 속한 메소드는 테스트에서 제외할 수도 있다.
- 아래 예는 "fast" 그룹에 속한 메소드만을 실행하며, "broken" 그룹에 속한 메소드는 실행에서 제외한다.
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="Simple Suite">
<test name="Test2">
<groups>
<run>
<include name="fast" />
<exclude name="broken" />
</run>
</groups>
</test>
</suite> |
4. 테스트 메소드 및 그룹간 종속성 지정을 통한 단위테스트 넘어서기
import org.testng.annotations.Test;
public class MethodDependencyTest {
@Test
public void serverStartedOk() {
System.out.println("serverStartedOk");
}
@Test(dependsOnMethods = { "serverStartedOk" })
public void testMethod1() {
System.out.println("testMethod1");
}
} |
import org.testng.annotations.Test;
public class GroupDependencyTest {
@Test(groups = { "init" })
public void serverStartedOk() {
System.out.println("serverStartedOk");
}
@Test(groups = { "init" })
public void initEnvironment() {
System.out.println("initEnvironment");
}
@Test(dependsOnGroups = { "init.*" })
public void testMethod1() {
System.out.println("testMethod1");
}
} |
- 그룹명 및 메소드명 지정시 정규표현식을 이용할 수 있다. 위 예에서 init.*는 init로 시작하는 모든 그룹을 의미한다.
5. 예외처리 테스트하기
import org.testng.annotations.Test;
public class ExceptionTest {
@Test(expectedExceptions = { ArithmeticException.class })
public void divisionByZero() {
int n = 2 / 0;
System.out.println(n);
}
@Test(expectedExceptions = { NumberFormatException.class })
public void parseInteger() {
int n = Integer.parseInt("two");
System.out.println(n);
}
} |
6. 타임아웃 테스트하기
import java.util.Random;
import org.testng.annotations.Test;
public class TimeOutTest {
@Test(timeOut = 2000)
public void testServer() throws Exception {
Random rand = new Random();
long responseTime = rand.nextInt(3000);
System.out.println(responseTime);
Thread.sleep(responseTime);
}
} |
7. 지정된 회수만큼 반복 테스트 수행하기
import org.testng.annotations.Test;
public class RepeatedTest {
@Test(invocationCount = 5)
public void testServer() {
System.out.println("accessPage");
}
} |
8. 지정된 쓰레드 개수만큼 테스트를 병렬로 수행하기
import java.util.Random;
import org.testng.annotations.Test;
public class ParallelRunningTest {
@Test(threadPoolSize = 5, invocationCount = 10, timeOut = 2000)
public void testServer() throws Exception {
System.out.println(Thread.currentThread().getId());
Random rand = new Random();
long responseTime = rand.nextInt(3000);
System.out.println(responseTime);
Thread.sleep(responseTime);
}
} |
9. 파라미터를 이용한 테스트 수행하기
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
public class ParameterTest {
@Parameters( { "first-name" })
@Test
public void testSingleString(String firstName) {
System.out.println("Invoked testString " + firstName);
assert "Sehwan".equals(firstName);
}
} |
- testng.xml 의 파라미터 설정 예는 아래와 같다.
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TestNG01">
<test name="Test1">
<parameter name="first-name" value="Sehwan" />
<classes>
<class name="testing.testng.ParameterTest" />
</classes>
</test>
</suite> |
10. DataProvider를 이용한 Data-Driven 테스트 수행하기
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderTest {
@Test(dataProvider = "dp")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
@DataProvider(name = "dp")
public Object[][] createData() {
return new Object[][] { { "John", new Integer(34) },
{ "Jane", new Integer(31) }, };
}
} |
- 테스트 메소드는 이 Data Provider를 이용하여 미리 정의된 데이터 집합을 가지고 반복적인 테스트를 수행할 수 있다.
import java.lang.reflect.Method;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderParamTest {
@Test(dataProvider = "dp")
public void testData1(String s) {
System.out.println(s);
}
@Test(dataProvider = "dp")
public void testData2(String s) {
System.out.println(s);
}
@DataProvider(name = "dp")
public Object[][] createData(Method m) {
System.out.println(m.getName());
return new Object[][] { { "Hello" } };
}
} |
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class DataProviderIteratorTest {
@Test(dataProvider = "dp")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
@DataProvider(name = "dp")
public Iterator<Object[]> createData() {
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[] {"John", new Integer(34)});
list.add(new Object[] {"Jane", new Integer(31)});
return list.iterator();
}
} |
- 이 방식은 Lazy Loading을 통해서 메모리 부족에 대한 좋은 해결책을 제공할 수 있다.
import org.testng.annotations.Test;
public class StaticDataProviderTest {
@Test(dataProvider = "dp", dataProviderClass = StaticDataProvider.class)
public void test(Integer n) {
System.out.println("Number:" + n);
}
} |
- 동일한 Data Provider를 이용하는 테스트 클래스가 여럿일 경우 유용하게 쓰일 수 있다.
import org.testng.annotations.DataProvider;
public class StaticDataProvider {
@DataProvider(name = "dp")
public static Object[][] createData() {
return new Object[][] { new Object[] { new Integer(42) } };
}
} |
테스트용 객체를 나타내는 용어 :: 2008/01/10 00:27
테스트용 객체를 나타내는 용어를 정리해 본다. Gerard Meszaros가 제공한 정의이다.
- Test Double is a generic term for any test object that replaces a production object.
- Dummy objects are passed around but not actually used. They are usually fillers for parameter lists.
- Fakes have working implementations, but take some shortcut (e.g., InMemoryDatabase).
- Stubs provide canned answers to calls made during a test.
- Mocks have expectations which form a specification of the calls they do and do not receive.
테스트의 목표 5가지 :: 2008/01/09 22:59
테스트의 목표 5가지
1. Testing should be part of the development process
2. Testing should be easy
3. Testing should be fast
4. Testing should be automated
5. Testing should be thorough
Test-Driven Development 적용 방법 :: 2008/01/07 20:11
자동화된 테스트로 개발을 이끌어 가는 개발 방식을 테스트 주도 개발(Test-Driven Development, TDD)이라 부른다. TDD는 분석 기술이며, 설계 기술이며, 개발의 모든 활동을 구조화하는 기술이다. 작동하는 깔끔한 코드(clean code that works). 이 핵심을 찌르는 한마디가 바로 테스트 주도 개발의 궁극적인 목표다.
TDD는 다음과 같은 순서로 진행을 하면 된다. 이 순서는 Kent Beck이 제시한 순서이다.
1. Quickly add a test
(테스트 프로그램을 작성한다.)
2. Run all tests and see the new one fail
(모든 테스트 프로그램을 수행시키고 테스트에 실패한 부분을 확인한다.)
3. Make a little change
(소스를 추가하거나 변경한다.)
4. Run all tests and see them all succeed
(다시 모든 테스트를 수행시키고 모두 테스트를 통과했는데 확인한다.)
5. Refactor to remove duplication
(중복을 제거하기 위해 Refactoring 한다.)
TDD는 다음과 같은 순서로 진행을 하면 된다. 이 순서는 Kent Beck이 제시한 순서이다.
1. Quickly add a test
(테스트 프로그램을 작성한다.)
2. Run all tests and see the new one fail
(모든 테스트 프로그램을 수행시키고 테스트에 실패한 부분을 확인한다.)
3. Make a little change
(소스를 추가하거나 변경한다.)
4. Run all tests and see them all succeed
(다시 모든 테스트를 수행시키고 모두 테스트를 통과했는데 확인한다.)
5. Refactor to remove duplication
(중복을 제거하기 위해 Refactoring 한다.)
예제를 통한 JUnit 4 살펴보기 :: 2008/01/06 03:45
JUnit 4는 JDK 5의 어노테이션 도입을 비롯해 예외처리 테스트, 타임아웃 등 TestNG로부터 많은 컨셉을 가져왔고, 거의 4년만에 이루어진 Major 릴리즈를 내놓았다. JUnit 3 패키지(junit.framework.*)를 그대로 포함하면서, 새로운 패키지(org.junit.*)를 추가한 구조를 가지고 있다. JUnit 3에서 한층 업그레이드 된 JUnit 4의 새로운 기능을 예제를 통해 알아본다.
1. 기본 테스트 예제import org.junit.Assert;
import org.junit.Test;
public class SimpleTest {
private int x = 3;
private int y = 2;
@Test
public void testAddition() {
int z = x + y;
Assert.assertEquals(5, z);
}
} |
- @Test를 사용하여 테스트 메소드를 정의하며, 메소드명의 test* 로 시작할 필요가 없다.
- 더이상 TestCase를 상속하지 않으므로, 별도 Assert 클래스로 제공하는 assert 메소드로 검증한다. 기존과 같은 형태의 코드 스타일을 원하면 Java 5에서부터 지원하는static 임포트를 사용하면 된다.
import static org.junit.Assert.*; ... assertEquals(5, z); ... |
2. 테스트 메소드 시작 전과 후에 처리 메소드 정의하기
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class BeforeTest {
private int x;
private int y;
@Before
public void doBeforeTest() {
x = 3;
y = 2;
System.out.println("@Before");
}
@Test
public void testAddition() {
int z = x + y;
assertEquals(5, z);
}
@Test
public void testSubtraction() {
int z = x - y;
assertEquals(1, z);
}
@After
public void doAfterTest() {
System.out.println("@After");
}
} |
3. 테스트 클래스 시작 전과 후에 처리 메소드 정의하기
import static org.junit.Assert.*;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class BeforeClassTest {
private static int x;
private static int y;
@BeforeClass
public static void doBeforeClass() {
x = 3;
y = 2;
System.out.println("@BeforeClass");
}
@Test
public void testAddition() {
int z = x + y;
assertEquals(5, z);
}
@Test
public void testSubtraction() {
int z = x - y;
assertEquals(1, z);
}
@AfterClass
public static void doAfterClass() {
System.out.println("@AfterClass");
}
} |
4. 주석을 이용해 Test Suite을 작성하여 테스트 실행하기
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses( { SimpleTest.class, BeforeTest.class, BeforeClassTest.class } )
public class AllTests {
} |
5. 예외처리 테스트하기
import org.junit.Test;
public class ExceptionTest {
@Test(expected = ArithmeticException.class)
public void divisionByZero() {
int n = 2 / 0;
System.out.println(n);
}
@Test(expected = NumberFormatException.class)
public void parseInteger() {
int n = Integer.parseInt("two");
System.out.println(n);
}
} |
6. 타임아웃 테스트하기
import org.junit.Test;
public class TimeoutTest {
@Test(timeout = 2000)
public void testTimeout() throws Exception {
Thread.sleep(1000); // success
//Thread.sleep(3000); // failure
}
} |
7. 파라미터를 이용하여 테스트하기
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class ParameterTest {
private int x;
private int y;
private int z;
public ParameterTest(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
@Parameters
public static Collection numValues() {
return Arrays.asList(new Object[][] { { 1, 2, 3 }, { 2, 5, 7 },
{ 3, 6, 9 }, { 10, 20, 30 } });
}
@Test
public void testAddition() {
System.out.println("x=" + x + ", y=" + y + ", z=" + z);
int n = x + y;
Assert.assertEquals(z, n);
}
} |
- Collection 유형을 반환하는 static 메소드를 작성하고, @Parameter로 파라미터 제공 메소드로 정의한다.
- 제공되는 파라미터를 받어 처리하는 생성자를 작성한다.
- 클래스 수준에서 테스트가 Parameterized 클래스와 함께 실행되도록 @RunWith 주석을 지정한다.
8. 특정 테스트 무시하기
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class IgnoreTest {
private int x = 3;
private int y = 2;
@Test
public void testAddition() {
int z = x + y;
Assert.assertEquals(5, z);
}
@Ignore("This metod isn't working yet.")
@Test
public void testSubtraction() {
//
}
} |
9. 배열 내용을 비교하는 assert 사용하기
import org.junit.Assert;
import org.junit.Test;
public class ArrayTest {
@Test
public void verifyArrayContents() throws Exception {
String[] expected = new String[] { "JUnit 3.8.2", "JUnit 4.4",
"TestNG 5.7" };
String[] actual = new String[] { "JUnit 3.8.2", "JUnit 4.4",
"TestNG 5.7" };
Assert.assertArrayEquals("the two arrays should not be equal",
expected, actual);
}
} |

