この文書の現在のバージョンと選択したバージョンの差分を表示します。
| java:test | java:test 2008/08/31 15:35 現在 | ||
|---|---|---|---|
| ライン 1: | ライン 1: | ||
| + | ====== テスティングフレームワーク ====== | ||
| + | 保守案件の改修やると、回帰テストの漏れが原因で重大な問題が発生することが最近多くなってきました。。\\ | ||
| + | そこで最近のテスティングフレームワークについて調査してみます。 | ||
| + | |||
| + | ===== JUnit4 ===== | ||
| + | JUnitの最新版はバージョン4でアノテーションに対応されました。\\ | ||
| + | 便利な機能が追加されたようなので、サンプルで動作確認をしてみます。\\ | ||
| + | 同様なフレームワークでTestNGというものもあるらしいですが、JUnit4はeclipse3.2から標準で対応しているので、導入のしやすさがメリットだと思います。 | ||
| + | |||
| + | ==== JUnit4のサンプルコード ==== | ||
| + | |||
| + | <code> | ||
| + | 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'); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | </code> | ||
| + | |||
| + | 上記テストクラスを実行すると。。\\ | ||
| + | @Ignoreのテストメソッドでは、コンストラクタのみで、@Before、@Test、@Afterは実行されません。\\ | ||
| + | また、テストが失敗すると、@Afterは実行されないようです。 | ||
| + | |||
| + | <code> | ||
| + | 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回だけ実行される。 | ||
| + | </code> | ||
| + | |||
| + | 以下のように@Ignoreは明示的に斜線が入って表示されています。\\ | ||
| + | また、methodD()は、1000ミリ秒を越えたのでテストが失敗しています。 | ||
| + | |||
| + | {{java:samplejunit4.jpg|}} | ||
| + | |||
| + | ==== 複数のテストクラスを一括で実行する ==== | ||
| + | |||
| + | 以下のように、@RunWithと@SuiteClassesを使います。 | ||
| + | |||
| + | <code> | ||
| + | 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 { | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ===== TestNG ===== | ||
| + | |||
| + | TestNGは、JUnitとよく似ていますが、JUnit4よりもアノテーション対応を先行していました。\\ | ||
| + | @BeforeClassはstaticでなくてもよいので、JUnitよりも柔軟性が高いです。あえて難点を言うと、\\ | ||
| + | Eclipseのテキストエディタからショートカットで実行できないくらいでしょうか^^; | ||
| + | |||
| + | ==== インストール ==== | ||
| + | |||
| + | JUnit4と違いEclipseには標準でインストールされていないので、TestNGのプラグインをEclipseの「ソフトウェア更新」でインストールしましょう。\\ | ||
| + | URLは、"http://beust.com/eclipse"です。\\ | ||
| + | また、jarファイルをダウンロードしてクラスパスに追加する必要がありますが、[[java:maven2]]なら以下の設定を<dependencies>に追加すれば準備OKです。 | ||
| + | |||
| + | <code> | ||
| + | <dependency> | ||
| + | <groupId>org.testng</groupId> | ||
| + | <artifactId>testng</artifactId> | ||
| + | <version>5.1</version> | ||
| + | <classifier>jdk15</classifier> | ||
| + | <scope>test</scope> | ||
| + | </dependency> | ||
| + | </code> | ||
| + | |||
| + | 準備ができたので、先ほどのJUnit4サンプルを元にテストコードを作ってみました。 | ||
| + | |||
| + | <code> | ||
| + | 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'); | ||
| + | } | ||
| + | |||
| + | } | ||
| + | </code> | ||
| + | |||
| + | 上記テストクラスの実行結果です。\\ | ||
| + | JUnit4と違い、コンストラクタは初回のみですね。。\\ | ||
| + | @BeforeClassなどの挙動は同じです。但し、テスト実行順序が不特定のようです。 | ||
| + | |||
| + | <code> | ||
| + | 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 | ||
| + | =============================================== | ||
| + | </code> | ||
| + | |||
| + | methodCは、@Testの属性"invocationCount"でメソッド実行回数を3に設定しています。 | ||
| + | |||
| + | {{java:sampletestng.jpg|}} | ||
| + | |||
| + | ==== 複数のテストクラスを一括で実行する ==== | ||
| + | |||
| + | 下記のテストクラスを追加して、先ほどのテストと一括に実行してみます。 | ||
| + | |||
| + | <code> | ||
| + | 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は、テストメソッド。"); | ||
| + | } | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | TestNGの場合、testng.xmlというファイルに実行方法の設定をします。 \\ | ||
| + | @Testのgroups属性で定義したグループのみ実行する場合は、以下のようにinculdeで指定します。 | ||
| + | |||
| + | <code> | ||
| + | <?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> | ||
| + | </code> | ||
| + | |||
| + | |||
| + | あとは、パッケージエクスプローラなどから、(testng.xmlを右クリック)→(実行)→(TestNG Suite)を指定すれば実行されます。 | ||
| + | |||
| + | {{java:sampletestngsuite.jpg|}} | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||
| + | |||