powerdee.com
Google
 
このサイト内 Web
 
カウンタ

テスティングフレームワーク

保守案件の改修やると、回帰テストの漏れが原因で重大な問題が発生することが最近多くなってきました。。 そこで最近のテスティングフレームワークについて調査してみます。

INDEX

JUnit4

JUnitの最新版はバージョン4でアノテーションに対応されました。便利な機能が追加されたようなので、早速サンプル作って動作確認です。
同様なフレームワークでTestNGというものもあるらしいですが、JUnit4はeclipse3.2から標準で対応しているので、導入のしやすさがメリットだと思います。

package junitsample;

import static org.junit.Assert.*;

import java.util.ArrayList;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

public class JUnit4SampleTest {

    public JUnit4SampleTest() {
        System.out.println(System.getProperty("line.separator") 
                + "JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。");
    }

    @BeforeClass
    public static void beforeClassTest() {
        System.out.println("beforeClassTest(): @BeforeClassは、最初の1回だけ実行される。");
    }

    @AfterClass
    public static void afterClassTest() {
        System.out.println("afterClassTest(): @AfterClassは、最後の1回だけ実行される。");
    }

    @Before
    public void beforeTest() {
        System.out.println("beforeTest(): @Beforeは、各テストメソッドの前に実行される。");
    }

    @After
    public void afterTest() {
        System.out.println("afterTest(): @Afterは、各テストメソッドの後に実行される。");
    }

    @Test
    public void methodA() {
        System.out.println("methodA(): @Testは、テストメソッド。");
    }

    @Ignore
    @Test
    public void methodB() {
        System.out.println("methodB(): @Ignoreは、テスト対象外になる。");
    }
    
    @Test(expected = IndexOutOfBoundsException.class)
    public void methodC() {
        System.out.println("methodC(): @Testの要素expectedで期待する例外クラスを指定する。");
        ArrayList list = new ArrayList();
        System.out.println(list.get(0));
    }
    
    @Test(timeout = 1000)
    public void methodD() {
        System.out.println("methodD(): @Testの要素timeoutで指定した時間(ミリ秒)でテストが終わるかテストする。");
//        for (int i = 0; i < 1000; i++) {}
        for (;;);
    }
    
    @Test
    public void methodE() {
        System.out.println("methodE(): assertXxxxxメソッドは org.junit.Assert を static importすれば使える。");
        String str = "JUnit4";
        assertEquals(str.charAt(5), '4');
    }
    
}

上記テストクラスを実行すると。。@Ignoreのテストメソッドでは、コンストラクタのみで、@Before、@Test、@Afterは実行されません。
また、テストが失敗すると、@Afterは実行されないようです。

beforeClassTest(): @BeforeClassは、最初の1回だけ実行される。

JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodA(): @Testは、テストメソッド。
afterTest(): @Afterは、各テストメソッドの後に実行される。

JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。

JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodC(): @Testの要素expectedで期待する例外クラスを指定する。
afterTest(): @Afterは、各テストメソッドの後に実行される。

JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodD(): @Testの要素timeoutで指定した時間(ミリ秒)でテストが終わるかテストする。

JUnit4SampleTest(): コンストラクタは、各テストメソッドの前に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodE(): assertXxxxxメソッドは org.junit.Assert を static importすれば使える。
afterTest(): @Afterは、各テストメソッドの後に実行される。
afterClassTest(): @AfterClassは、最後の1回だけ実行される。

以下のように@Ignoreは明示的に斜線が入って表示されています。 また、methodD()は、1000ミリ秒を越えたのでテストが失敗しています。


複数のテストクラスを一括で実行する

以下のように、@RunWithと@SuiteClassesを使います。

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({JUnit4SampleTest.class, JUnit4SampleTest2.class})
public class AllTests {
}

TestNG

TestNGは、JUnitとよく似ていますが、JUnit4よりもアノテーション対応を先行していました。@BeforeClassはstaticでなくてもよいので、JUnitよりも柔軟性が高いです。 あえて難点を言うと、Eclipseのテキストエディタからショートカットで実行できないくらいでしょうか^^;

インストール

JUnit4と違いEclipseには標準でインストールされていないので、TestNGのプラグインをEclipseの「ソフトウェア更新」でインストールしましょう。URLは、"http://beust.com/eclipse"です。

また、jarファイルをダウンロードしてクラスパスに追加する必要がありますが、maven2なら以下の設定を<dependencies>に追加すれば準備OKです。

    <dependency>
      <groupId>org.testng</groupId>
      <artifactId>testng</artifactId>
      <version>5.1</version>
      <classifier>jdk15</classifier>
      <scope>test</scope>
    </dependency>

準備ができたので、先ほどのJUnit4サンプルを元にテストコードを作ってみました。

package com.powerdee.sample.test;

import static org.testng.Assert.assertEquals;

import java.util.ArrayList;

import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class TestNgSampleTest1 {

    public TestNgSampleTest1() {
        System.out.println(System.getProperty("line.separator")
                + "TestNgSampleTest1(): コンストラクタは、最初の1回だけ実行される。");
    }
    
    @BeforeClass
    public void beforeClass() {
        System.out.println("beforeClassTest(): @BeforeClassは、最初の1回だけ実行される。");
    }
    
    @AfterClass
    public static void afterClassTest() {
        System.out.println("afterClassTest(): @AfterClassは、最後の1回だけ実行される。");
    }

    @BeforeMethod
    public void beforeTest() {
        System.out.println("beforeTest(): @Beforeは、各テストメソッドの前に実行される。");
    }

    @AfterMethod
    public void afterTest() {
        System.out.println("afterTest(): @Afterは、各テストメソッドの後に実行される。");
    }

    @Test(groups = { "groupA" })
    public void methodA() {
        System.out.println("methodA(): @Testは、テストメソッド。(groupA)");
    }

    @Test(expectedExceptions = IndexOutOfBoundsException.class)
    public void methodB() {
        System.out.println("methodB(): @Testの要素expectedExceptionsで期待する例外クラスを指定する。");
        ArrayList list = new ArrayList();
        System.out.println(list.get(0));
    }
    
    @Test(timeOut = 1000, invocationCount = 3, threadPoolSize = 1)
    public void methodC() {
        System.out.println("methodC(): @Testの要素timeOutで指定した時間(ミリ秒)でテストが終わるかテストする。");
        for (int i = 0; i < 1000; i++) {}
    }
    
    @Test
    public void methodD() {
        System.out.println("methodD(): assertXxxxxメソッドは org.testng.Assert を static importすれば使える。");
        String str = "TestNG";
        assertEquals(str.charAt(5), 'G');
    }
    
}

上記テストクラスの実行結果です。JUnit4と違い、コンストラクタは初回のみですね。。@BeforeClassなどの挙動は同じです。但し、テスト実行順序が不特定のようです。

TestNgSampleTest1(): コンストラクタは、最初の1回だけ実行される。
beforeClassTest(): @BeforeClassは、最初の1回だけ実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodC(): @Testの要素timeOutで指定した時間(ミリ秒)でテストが終わるかテストする。
afterTest(): @Afterは、各テストメソッドの後に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodC(): @Testの要素timeOutで指定した時間(ミリ秒)でテストが終わるかテストする。
afterTest(): @Afterは、各テストメソッドの後に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodC(): @Testの要素timeOutで指定した時間(ミリ秒)でテストが終わるかテストする。
afterTest(): @Afterは、各テストメソッドの後に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodA(): @Testは、テストメソッド。(groupA)
afterTest(): @Afterは、各テストメソッドの後に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodD(): assertXxxxxメソッドは org.testng.Assert を static importすれば使える。
afterTest(): @Afterは、各テストメソッドの後に実行される。
beforeTest(): @Beforeは、各テストメソッドの前に実行される。
methodB(): @Testの要素expectedExceptionsで期待する例外クラスを指定する。
afterTest(): @Afterは、各テストメソッドの後に実行される。
afterClassTest(): @AfterClassは、最後の1回だけ実行される。
PASSED: methodC
PASSED: methodC
PASSED: methodC
PASSED: methodA
PASSED: methodD
PASSED: methodB

===============================================
    com.powerdee.sample.test.TestNgSampleTest1
    Tests run: 6, Failures: 0, Skips: 0
===============================================

methodCは、@Testの属性"invocationCount"でメソッド実行回数を3に設定しています。


複数のテストクラスを一括で実行する

下記のテストクラスを追加して、先ほどのテストと一括に実行してみます。

package com.powerdee.sample.test;

import org.testng.annotations.Test;

public class TestNgSampleTest2 {

    public TestNgSampleTest2() {
        System.out.println(System.getProperty("line.separator")
                + "TestNgSampleTest2(): コンストラクタは、最初の1回だけ実行される。");
    }

    @Test(groups = { "groupA" })
    public void methodE() {
        System.out.println("methodE(): @Testは、テストメソッド。(groupA)");
    }

    @Test
    public void methodF() {
        System.out.println("methodF(): @Testは、テストメソッド。");
    }
}

TestNGの場合、testng.xmlというファイルに実行方法の設定をします。 @Testのgroups属性で定義したグループのみ実行する場合は、以下のようにinculdeで指定します。

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">

<suite name="TestNgSuite" verbose="1">
  <test name="TestNgSamples">
    <groups>
      <run>
        <include name="groupA" />
      </run>
    </groups>
    <classes>
      <class name="com.powerdee.sample.test.TestNgSampleTest1"/>
      <class name="com.powerdee.sample.test.TestNgSampleTest2"/>
    </classes>
  </test>
</suite>

あとは、パッケージエクスプローラなどから、(testng.xmlを右クリック)→(実行)→(TestNG Suite)を指定すれば実行されます。



おすすめ書籍


開発者ノートシリーズMaven (開発者ノートシリーズ)

著者:ビンセント マソル、ティモシー・M. オブライエン
出版社:オライリージャパン(2006-02)
価格:¥2,520(税込)
ダイアグラム別 UML徹底活用 (DB Magazine SELECTION)

著者:井上 樹
出版社:翔泳社(2005-06-21)
価格:¥2,520(税込)
Apache Maven 2.0入門 Java・オープンソース・ビルドツール

著者:野瀬 直樹、横田 健彦
出版社:技術評論社(2006-12-13)
価格:¥2,499(税込)
現場のUML―モデルベース開発のすべて

著者:浅井 麻衣、橋本 大輔、藤井 啓詞、重田 正俊、浜口 弘志、桐越 信一、長瀬 嘉秀
出版社:ソーテック社(2006-12)
価格:¥3,024(税込)



ページTopへ / ▲Homeへ