単体テスト
JUnit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
テスト対象
public class Target{
public int divid(int a, int b){
return a / b;
}
}
テストコード(JUnit 4)
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
public class TargetTest{
private Target target = new Target();
// すべてのメソッドの前処理
@BeforeClass
public static void init(){ // staticメソッドが必要
// do something
}
// すべてのメソッドの後処理
@AfterClass
public static void dispose(){ // staticメソッドが必要
// do something
}
// 毎メソッドの前処理
@Before
public void before(){
// do something
}
// 毎メソッドの後処理
@After
public void after(){
// do something
}
// 正常系テストケース
@Test
public void testNormal(){
assertEquals(10, target.divid(100, 10));
}
// 境界線テストケース
@Test
public void testBorder(){
assertEquals(0, target.divid(Integer.MAX_VALUE, Integer.MIN_VALUE));
assertEquals(-1, target.divid(Integer.MIN_VALUE, Integer.MAX_VALUE));
}
// 異常系テストケース
@Test(expected=ArithmeticException.class)
public void testException(){
target.divid(100, 0);
}
@Ignore // 実行しない
@Test(timeout=100) // 100ms内に実行完了
public void testOther(){
// do something
}
}
★Testing privateメソッド(By Reflection)
@Test
public void test() throws Exception {
Method method
= Sample.class.getDeclaredMethod(
"targetMethod", String.class, String.class);
method.setAccessible(true);
Sample sample = new Sample();
String result = (String) method.invoke(sample, "xxx", "yyy");
assertEquals("expected", result);
}
★Springテスト
import org.junit.runner.RunWith;
import org.junit.Test;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.annotation.Timed;
import org.springframework.beans.factory.annotation.Autowired;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/spring-test.xml"})
public class MyServiceTest {
@Autowired
private MyService myService;
@Test
@Timed(millis=1000)
public void testXxx() {
...
}
}
★EJB(JavaEE 6)テスト
import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;
public class BookEJBTest {
private static EJBContainer ec;
private static Context ctx;
@BeforeClass
public static void initContainer() throws Exception {
ec = EJBContainer.createEJBContainer();
ctx = ec.getContext();
}
@AfterClass
public static void closeContainer() throws Exception {
if (ec != null) {
ec.close();
}
}
@Test
public void testCreateBook() throws NamingException {
Book book = new Book();
book.setTitle("xxx");
...
BookEJB bookEJB =
(BookEJB) ctx.lookup("java:global/classes/BookEJB");
book = bookEJB.createBook(book);
assertNotNull(book.getId());
List<Book> books = bookEJB.findBooks();
assertNotNull(books);
}
}
private Context ctx;
@Before
public void setUp() throws Exception {
ctx = EJBContainer.createEJBContainer().getContext();
ctx.bind("inject", this);
}
@After
public void cleanUp() throws Exception {
ctx.unbind("inject");
ctx.close();
}
あるいは
private EJBContainer ec;
@Before
public void setUp() throws Exception {
ec = EJBContainer.createEJBContainer();
ec.getContext().bind("inject", this);
}
@After
public void cleanUp() throws Exception {
ec.close();
}
Matcher API
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.*;
assertThat(actual, is(expected));
assertThat(actual, is(not(expected)));
assertThat(actual, is(nullValue()));
assertThat(actual, is(notNullValue()));
assertThat(actual, is(sameInstance(expected)));
assertThat(actual, is(instanceOf(Serializable.class)));
import static org.hamcrest.Matchers.*;
List<String> actual = ...;
assertThat(actual, hasItem("xxx"));
assertThat(actual, hasItems("xxx", "yyy"));
★Hamcrest matchers
assertThat(actualEmployee, is(equalToEmployee(expectedEmployee)));
public static Matcher equalToEmployee(Employee employee) {
return new EmployeeMatcher(employee);
}
private static class EmployeeMatcher extends TypeSafeDiagnosingMatcher {
private final Matcher firstName;
private final Matcher lastName;
public EmployeeMatcher(Employee employee) {
this.firstName = equalTo(employee.getFirstName());
this.lastName = equalTo(employee.getLastName());
}
@Override
protected boolean matchesSafely(Employee item, Description mismatchDescription) {
boolean matches = true;
mismatchDescription.appendText("{ ");
if (!firstName.matches(item.getFirstName())) {
mismatchDescription.appendText(" ").appendText("firstName").appendText(" ");
firstName.describeMismatch(item.getFirstName(), mismatchDescription);
matches = false;
}
if (!lastName.matches(item.getLastName())) {
mismatchDescription.appendText(" ").appendText("lastName").appendText(" ");
lastName.describeMismatch(item.getLastName(), mismatchDescription);
matches = false;
}
mismatchDescription.appendText(" }");
return matches;
}
@Override
public void describeTo(Description description) {
description.appendText("{");
description.appendText(" firstName ").appendDescriptionOf(this.firstName);
description.appendText(" lastName ").appendDescriptionOf(this.lastName);
description.appendText(" }");
}
}
NUnit
テスト対象
public class Target
{
public int Divid(int a, int b)
{
return a / b;
}
}
テストコード
using NUnit.Framework;
namespace com.example.Testers
{
[TestFixture]
class XXXTester
{
// すべてのメソッドの前処理
[TestFixtureSetUp]
public void Init()
{
// do something
}
// すべてのメソッドの後処理
[TestFixtureTearDown]
public void Dispose()
{
// do something
}
// 毎メソッドの前処理
[SetUp]
public void SetUp()
{
Target target = new Target();
}
// 正常系テストケース
[Test]
public void TestNormal()
{
Assert.AreEqual(10, target.Divid(100, 10));
}
// 異常系テストケース
[Test]
[ExpectedException(typeof(DivideByZeroException))]
// 或いは[Test, ExpectedException(typeof(DivideByZeroException))]
public void TestException()
{
target.Divid(100, 0);
}
}
}
TestNG
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
@BeforeSuite/@AfterSuite テスト・スイート内ののすべてのテストが対象
@BeforeTest/@AfterTest テスト
@BeforeClass/@AfterClass テストクラス内のすべてのテストが対象
@BeforeMethod/@AfterMethod テストメソッド
@DataProvider テストメソッドにデータを供給する
@Factory オブジェクトのファクトリメソッド
@Parameters テストメソッドにパラメータを渡す
@Testの属性
dataProvider データ供給
expectedExceptions 期待する例外
groups 所属するグループ
invocationCount 実行回数
timeOut タイムアウト時間(ミリ秒)
threadPoolSize スレッドプールサイズ(invocationCountが未設定の場合、無視される)
enabled 実行可否
dependsOnMethods 依存するテストメソッド
dependsOnGroups 依存するグループ
★Execution Procedure
@BeforeSuite
@BeforeTest
@BeforeClass
@BeforeMethod
@Test
@AfterMethod
@BeforeMethod
@Test
@AfterMethod
@AfterClass
@AfterTest
@AfterSuite
testng.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1">
<test name="test1">
<classes>
<class name="xxx"/>
</classes>
</test>
</suite>
★サンプル
テスト対象
public class MyService {
public int add(int a, int b) {
return a + b;
}
public void throwException() {
throw new RuntimeException("例外が発生しました");
}
}
テストコード
import static org.testng.Assert.*;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class MyServiceTest {
private MyService myService;
@BeforeClass
public static void setUpClass() {
...
}
@AfterClass
public static void tearDownClass() {
...
}
@BeforeMethod
public void init() {
myService = new MyService();
}
@Test(expectedExceptions = { RuntimeException.class })
public void testException() {
myService.throwException();
}
@DataProvider(name = "add")
public Object[][] data() {
return new Object[][] {
{ 1, 2, 3 }, { 3, 3, 6 }, { 6, 5, 11 } };
}
@Test(dataProvider = "add")
public void testAdd(int a, int b, int expected) {
assertNotNull(myService);
int actual = myService.add(a, b);
assertEquals(actual, expected);
}
@DataProvider(name = "data")
public Object[][] dataProvider() {
User user = new User();
user.setId(101);
user.setName("Andy");
return new Object[][]{
{user}
};
}
@Test(dataProvider = "data")
public void test(User user) {
...
}
@Test(timeOut=1000, invocationCount=10, threadPoolSize=5)
public void loadPerformance() throws Exception {
System.out.printf(
"%n Thread Id : %s ", Thread.currentThread().getId());
...
}
}
★Using @DataProvider and Test in Different Class
public class DataProviderClass {
@DataProvider(name = "data-provider")
public static Object[][] dataProviderMethod() {
return new Object[][] { { "one" }, { "two" } };
}
}
public class TestClass {
@Test(dataProvider = "data-provider", dataProviderClass = DataProviderClass.class)
public void testMethod(String data) {
...
}
}
★@DataProvider vs @Factory
@BeforeClass
public void beforeClass() {
out.println("beforeClass() executed");
}
@Test(dataProvider = "dataMethod")
public void testMethod(String param) {
out.println("testMethod() executed: " + param);
}
@DataProvider
private Object[][] dataMethod() {
return new Object[][] { { "one" }, { "two" } };
}
beforeClass() executed
testMethod() executed: one
testMethod() executed: two
private String param;
@Factory(dataProvider = "dataMethod")
public SampleTest(String param) {
this.param = param;
}
@DataProvider
private static Object[][] dataMethod() {
return new Object[][] { { "one" }, { "two" } };
}
@BeforeClass
public void beforeClass() {
out.println("beforeClass() executed");
}
@Test
public void testMethod() {
out.println("testMethod() executed: " + param);
}
beforeClass() executed
testMethod() executed: one
beforeClass() executed
testMethod() executed: two