この文書は、JUnit4をやってみようの続きです。JUnit4
のRulesについてjunit-4.11.jar
をベースに動かしてみた結果をまとめています。
この文書は技術的に正確であることを意図して書いていますが、どこかで大嘘をついていたり、経年により陳腐化しているかもしれません。
もっと有効な方法があることを見逃しているかもしれません。
姉妹ページ、JUnit4をやってみよう、JUnit4をもっとやってみようもどうぞ。
サンプルソースはhttps://github.com/kazurof/tryjunit4 においてあります。
Rulesは、テストを実行する際の共通処理を設定する枠組みです。この枠組みに沿った便利機能が幾つか提供されています。 まずはその挙動に慣れれば仕組みがイメージできると思います。
TemporaryFolderは、テスト実行時のみ使えて終わったら消されるファイル・フォルダを提供します。
package tryjunit4.rule; import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.JUnitCore; public class TemporaryFolderTest { public static void main(String[] args) { System.out.printf("java.io.tmpdir is %s%n", System.getProperty("java.io.tmpdir")); JUnitCore.main(TemporaryFolderTest.class.getName()); } @Rule public TemporaryFolder folder = new TemporaryFolder(); @Test public void testUsingTempFolder() throws IOException { tryTemporaryFolder(); } @Test public void testUsingTempFolder2() throws IOException { tryTemporaryFolder(); } private void tryTemporaryFolder() throws IOException { File createdFile = folder.newFile("myfile.txt"); File createdFolder = folder.newFolder("subfolder"); File randomFile = folder.newFile(); assertTrue(createdFolder.isDirectory()); assertTrue(createdFile.isFile()); assertTrue(randomFile.isFile()); System.out.println("\n-------------"); System.out.println("root folder is ->" + folder.getRoot()); System.out.println(createdFile.getAbsolutePath()); System.out.println(createdFolder.getAbsolutePath()); System.out.println(randomFile.getAbsolutePath()); System.out.println(); } }
実行結果(CLASSPATHに、junit-4.11.jar
が設定されているとする。以下の実行例も同様。)
$ java tryjunit4.rule.TemporaryFolderTest java.io.tmpdir is /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/ JUnit version 4.11 . ------------- root folder is ->/var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit4919826527625498940 /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit4919826527625498940/myfile.txt /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit4919826527625498940/subfolder /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit4919826527625498940/junit8388176439118689457.tmp . ------------- root folder is ->/var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit1950742328813729764 /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit1950742328813729764/myfile.txt /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit1950742328813729764/subfolder /var/folders/yO/yODr4DYqEMOo8qc2HB+S8E+++TI/-Tmp-/junit1950742328813729764/junit6746419094246856591.tmp Time: 0.065 OK (2 tests) $
20行目でfolderフィールドを定義し、 TemporaryFolder のインスタンスを設定します。さらに Rule アノテーションを付与します。 JUnitフレームワークはこのアノテーションが付いているフィールドのインスタンスに共通処理を委譲します。 実は、Rule アノテーションはフィールドだけでなく、メソッドにも付与できます。この場合メソッドの戻り値に共通処理を委譲します。 フィールドの型・メソッドの戻り値の型は、 TestRule でなくてはなりません。
33,34,35行目で、folderフィールド経由でテンポラリファイル・フォルダを作っています。具体的な場所は、java.io.tmpdir
システムプロパティの
パス配下のようですね。(15行目の出力と比較。)この例はMacでの出力結果ですが、windowsやLinuxではまた違ってくると思います。
処理の流れとしては、
という流れになります。実際に削除されていることはこの文書では説明しにくいですが、実際に動作させてみてブレークポイントで止めながら
動かすと確認できると思います。
テスト毎に一時的な使い捨てのファイルを使いたいときに便利かと思います。
ExpectedExceptionは、スローされるべき例外を管理する機能です。
package tryjunit4.rule; import static org.hamcrest.CoreMatchers.startsWith; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.JUnitCore; public class ExpectedExceptionTest { public static void main(String[] args) { JUnitCore.main(ExpectedExceptionTest.class.getName()); } @Rule public ExpectedException thrown = ExpectedException.none(); @Test(expected = NullPointerException.class) public void ordinalExpectedException() { throw new NullPointerException(); } @Test public void throwsNullPointerException() { thrown.expect(NullPointerException.class); throw new NullPointerException(); } @Test public void throwsNullPointerExceptionWithMessage() { thrown.expect(NullPointerException.class); thrown.expectMessage("happened?"); thrown.expectMessage(startsWith("What")); throw new NullPointerException("What happened?"); } @Test public void throwsNothing() { // no exception expected, none thrown: passes. } }
実行結果
$ java tryjunit4.rule.ExpectedExceptionTest JUnit version 4.11 .... Time: 0.016 OK (4 tests) $
従来、例外がスローされることを期待するテストについては、18行目のように、
@Test(expected = NullPointerException.class)
といった記述をしていました。しかしこれでテストできるのは例外オブジェクトの型だけで、例外メッセージの内容へのテストができません。
他に独自の情報を例外オブジェクトに持たせていた場合もやはりテストができません。
すると、JUnit3.*時代のようにtry-catchを書いてcatch節で例外オブジェクトの内容のテストと、
try節の最後の行にAssert.fail()
を書く形になります。あまり便利とは言いがたいです。
ExpectedExceptionでこの状況を解決出来ます。 ExpectedException#expect()で期待する例外の型を、(30行目)ExpectedException#expectMessage()で期待する例外メッセージを(32行目)を 設定出来ます。また、 Matcherを設定することができますのでassertThatの仕組みを使ってより柔軟な 例外オブジェクトのテストが可能です。(33行目)何も設定されなかったら例外がスローされないことをテストします。(38行目) 便利ですね。
ExpectedException#expect()などのメソッド呼び出しはテストメソッドの先頭で呼ぶ必要があります。 (例外がスローされたら残りの処理は実行されないから。)もしプロジェクトで、「1.テストデータの設定」「2.ビジネスロジックの実行」「3.結果の検証」という テストケースの書き方について標準化がされていたら、「1.データの設定」の段階でExpectedException#expect()を呼ぶ必要があることに留意する必要があるかもしれません。 (考えすぎ?)検証時に期待する結果を予め登録するという形です。
ErrorCollectorは、1つのテストで複数の例外がスローされる機能を提供します。
package tryjunit4.rule; import static org.hamcrest.CoreMatchers.is; import java.util.concurrent.Callable; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ErrorCollector; import org.junit.runner.JUnitCore; public class ErrorCollectorTest { public static void main(String[] args) { JUnitCore.main(ErrorCollectorTest.class.getName()); } @Rule public ErrorCollector collector = new ErrorCollector(); @Test public void testAddError() { collector.addError(new Throwable("first thing went wrong")); collector.addError(new Throwable("second thing went wrong")); } @Test public void testCheckThat() { collector.checkThat("apple", is("appleeeee")); System.out.println("test execution came to the last line of testCheckThat()!"); } @Test public void testCallable() { collector.checkSucceeds(new Callable<Object>() { @Override public String call() throws Exception { throw new RuntimeException("FAIL!"); } }); System.out.println("test execution came to the last line of testCheckThat()!"); } }
実行結果(スタックトレースが長いので適宜省略します。)
$ java tryjunit4.rule.ErrorCollectorTest JUnit version 4.11 .EE.test execution came to the last line of testCheckThat()! E.test execution came to the last line of testCheckThat()! E Time: 0.025 There were 4 failures: 1) testAddError(tryjunit4.rule.ErrorCollectorTest) java.lang.Throwable: first thing went wrong at tryjunit4.rule.ErrorCollectorTest.testAddError(ErrorCollectorTest.java:23) ーー省略ーー at tryjunit4.rule.ErrorCollectorTest.main(ErrorCollectorTest.java:15) 2) testAddError(tryjunit4.rule.ErrorCollectorTest) java.lang.Throwable: second thing went wrong at tryjunit4.rule.ErrorCollectorTest.testAddError(ErrorCollectorTest.java:24) ーー省略ーー at tryjunit4.rule.ErrorCollectorTest.main(ErrorCollectorTest.java:15) 3) testCallable(tryjunit4.rule.ErrorCollectorTest) java.lang.RuntimeException: FAIL! at tryjunit4.rule.ErrorCollectorTest$1.call(ErrorCollectorTest.java:38) at tryjunit4.rule.ErrorCollectorTest$1.call(ErrorCollectorTest.java:1) at org.junit.rules.ErrorCollector.checkSucceeds(ErrorCollector.java:78) at tryjunit4.rule.ErrorCollectorTest.testCallable(ErrorCollectorTest.java:35) ーー省略ーー at tryjunit4.rule.ErrorCollectorTest.main(ErrorCollectorTest.java:15) 4) testCheckThat(tryjunit4.rule.ErrorCollectorTest) java.lang.AssertionError: Expected: is "appleeeee" but: was "apple" at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20) at org.junit.Assert.assertThat(Assert.java:865) at org.junit.rules.ErrorCollector$1.call(ErrorCollector.java:65) at org.junit.rules.ErrorCollector.checkSucceeds(ErrorCollector.java:78) at org.junit.rules.ErrorCollector.checkThat(ErrorCollector.java:63) at org.junit.rules.ErrorCollector.checkThat(ErrorCollector.java:54) at tryjunit4.rule.ErrorCollectorTest.testCheckThat(ErrorCollectorTest.java:29) ーー省略ーー at tryjunit4.rule.ErrorCollectorTest.main(ErrorCollectorTest.java:15) FAILURES!!! Tests run: 3, Failures: 4 $
1つのテストメソッドが失敗する場合、JUnitでは Throwable がスローされることで表現されます。 しかし多数の項目を一度にテストする場合、(例えば表形式など多数のデータが一つになっているオブジェクトのテストなど) スローされる例外が1個だけというのは不便です。データの間違いがあったらすぐテスト失敗にして残りのデータをテストしない(まだ間違いがあるかもしれないのに) という動作では、結局全部でいくつ間違いがあるのかわからなくなります。全体としていくつ間違えているのか知りたいのです。
ErrorCollectorは、複数の例外オブジェクトを受け付けてくれます。テスト終了後何らかの例外オブジェクトが 渡されていたらテストを失敗にすることができます。さらに、テスト対象オブジェクトと Matcherを設定することができますので、 assertThatの仕組みを使ってより柔軟なテストが可能です。
またさらに、Callableのオブジェクトを渡して例外がスローされるかのテストや、Callableの結果をテストすることもできます。 「エラーを収集するもの」の意味から外れている感じもありますね。しかし、CallableオブジェクトのテストをするAPIをゼロから設計したとすると、名前はともかくこのような形になったかもしれません。 ErrorCollectorというクラスにこのAPIが付いたのは単なる偶然とも言えるかもしれません。
閑話休題。多数のテストを1個のメソッドで大量に行うとそれはそれで大変という向きもあるかもしれませんが、 ひと通りテストをして失敗毎に例外オブジェクトを保持しておく便利機能はフレームワークにあったほうが良いと思います。
TestNameは、現在実行しているテストの名前を提供します。
package tryjunit4.rule; import static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import org.junit.runner.JUnitCore; public class TestNameTest { public static void main(String[] args) { JUnitCore.main(TestNameTest.class.getName()); } @Rule public TestName name = new TestName(); @Test public void testA() { assertEquals("testA", name.getMethodName()); someMethod(); } @Test public void testB() { assertEquals("testB", name.getMethodName()); someMethod(); } private void someMethod() { System.out.println(name.getMethodName()); } }
実行結果
$ java tryjunit4.rule.TestNameTest JUnit version 4.11 .testA .testB Time: 0.006 OK (2 tests) $
TestNameは、現在実行しているテストの名前を提供します。それだけです。 テスト名によって制御を変えたいときとかなにかログに出したいときに便利かもしれません。 何らかのJUnitの拡張ライブラリを開発する際のデバッグ目的など、特殊用途向けかも知れません。
Timeoutは、長時間かかるテストを失敗とみなします。
package tryjunit4.rule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.rules.Timeout; import org.junit.runner.JUnitCore; public class TimeoutTest { public static void main(String[] args) { JUnitCore.main(TimeoutTest.class.getName()); } @Rule public TestRule globalTimeout = new Timeout(20); @Test public void testInfiniteLoop1() { for (;;) { } } @Test(timeout = 10) public void testInfiniteLoop2() { for (;;) { } } @Test(timeout = 30) public void testInfiniteLoop3() { for (;;) { } } }
実行結果
$ java tryjunit4.rule.TimeoutTest JUnit version 4.11 .E.E.E Time: 0.102 There were 3 failures: 1) testInfiniteLoop1(tryjunit4.rule.TimeoutTest) java.lang.Exception: test timed out after 20 milliseconds at tryjunit4.rule.TimeoutTest.testInfiniteLoop1(TimeoutTest.java:19) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.FailOnTimeout$StatementThread.run(FailOnTimeout.java:74) 2) testInfiniteLoop2(tryjunit4.rule.TimeoutTest) java.lang.Exception: test timed out after 10 milliseconds at tryjunit4.rule.TimeoutTest.testInfiniteLoop2(TimeoutTest.java:25) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.FailOnTimeout$StatementThread.run(FailOnTimeout.java:74) 3) testInfiniteLoop3(tryjunit4.rule.TimeoutTest) java.lang.Exception: test timed out after 20 milliseconds at java.lang.Object.wait(Native Method) at java.lang.Thread.join(Thread.java:1218) at org.junit.internal.runners.statements.FailOnTimeout.evaluateStatement(FailOnTimeout.java:26) at org.junit.internal.runners.statements.FailOnTimeout.evaluate(FailOnTimeout.java:17) at org.junit.internal.runners.statements.FailOnTimeout$StatementThread.run(FailOnTimeout.java:74) FAILURES!!! Tests run: 3, Failures: 3 $
従前、テストメソッドのタイムアウトは、 Testアノテーションに設定することが出来ました。
@Test(timeout = 30)
Timeoutではクラスごとに同じ設定をすることができます。メソッド毎に書く必要がないので記述が簡素化できますね。 Test アノテーションにもタイムアウト設定があった場合はより短い方の時間が優先されるようです。
自前Ruleを作成する際に使える便利な抽象クラスがいくつかあります。今まで紹介したRuleの一部でも使われています。 簡単な例を紹介します。
ExternalResourceは、テストの前処理と後処理を行うためのメソッドを提供します。TemporaryFolderの親クラスです。
package tryjunit4.rule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.JUnitCore; public class ExternalResourceTest { public static void main(String[] args) { JUnitCore.main(ExternalResourceTest.class.getName()); } @Rule public ExternalResource resource = new ExternalResource() { @Override protected void before() { System.out.println("I am before() method."); }; @Override protected void after() { System.out.println("I am after() method."); }; }; @Test public void testNantoka() { System.out.println("I am testNantoka() method."); } @Test public void testKantoka() { System.out.println("I am testKantoka() method."); } }
実行結果
$ java tryjunit4.rule.ExternalResourceTest JUnit version 4.11 .I am before() method. I am testKantoka() method. I am after() method. .I am before() method. I am testNantoka() method. I am after() method. Time: 0.005 OK (2 tests) $
@BeforeClass,@Before,@AfterClass,@Afterそれぞれの違いを検証する。で検証した@Before と@Afterと同様の挙動になります。
TestWatcherは、テスト実行前、前提条件不成立時、テスト成功時、テスト失敗時、テスト終了後で呼び出されるメソッドを提供します。 TestNameの親クラスです。
package tryjunit4.rule; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import org.junit.Rule; import org.junit.Test; import org.junit.internal.AssumptionViolatedException; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.junit.runner.JUnitCore; public class TestWatcherTest { public static void main(String[] args) { JUnitCore.main(TestWatcherTest.class.getName()); } @Rule public TestWatcher watchman = new TestWatcher() { @Override protected void starting(Description d) { System.out.println("I am starting() method. name -> " + d.getMethodName()); } @Override protected void skipped(AssumptionViolatedException e, Description d) { System.out.println("I am skipped() method. name -> " + d.getMethodName()); System.out.println(e.toString()); } @Override protected void succeeded(Description d) { System.out.println("I am succeeded() method. name -> " + d.getMethodName()); } @Override protected void failed(Throwable th, Description d) { System.out.println("I am failed() method. name -> " + d.getMethodName()); System.out.println(th.toString()); } @Override protected void finished(Description d) { System.out.println("I am finished() method. name -> " + d.getMethodName()); } }; @Test public void testAndFail() { fail("this test is fail."); } @Test public void testAndSucceed() { } @Test public void testPreconditionViolation() { assumeTrue(false); } }
実行結果(スタックトレースが長いので適宜省略します。)
$ java tryjunit4.rule.TestWatcherTest JUnit version 4.11 .I am starting() method. name -> testAndSucceed I am succeeded() method. name -> testAndSucceed I am finished() method. name -> testAndSucceed .I am starting() method. name -> testAndFail I am failed() method. name -> testAndFail java.lang.AssertionError: this test is fail. I am finished() method. name -> testAndFail E.I am starting() method. name -> testPreconditionViolation I am skipped() method. name -> testPreconditionViolation org.junit.internal.AssumptionViolatedException: got:, expected: is I am finished() method. name -> testPreconditionViolation Time: 0.018 There was 1 failure: 1) testAndFail(tryjunit4.rule.TestWatcherTest) java.lang.AssertionError: this test is fail. at org.junit.Assert.fail(Assert.java:88) at tryjunit4.rule.TestWatcherTest.testAndFail(TestWatcherTest.java:52) ーー省略ーー at tryjunit4.rule.TestWatcherTest.main(TestWatcherTest.java:15) FAILURES!!! Tests run: 3, Failures: 1 $
各時点でのメソッドで、
DescriptionのインスタンスをJUnitフレームワーク側から貰えますので、
このインスタンスの情報を利用できます。
void skipped(AssumptionViolatedException e, Description d)
については説明が必要かもしれません。(JUnit4.11での新メソッドです。)
このメソッドはテストの前提条件が失敗した時に、すなわちassume系のテストメソッドが失敗した時に呼び出されます。assume系の詳細は
Assumptionsを試すをどうぞ。
Verifierはテスト終了後の検証を行うメソッドを提供します。ErrorCollectorの親クラスです。
package tryjunit4.rule; import static org.junit.Assert.assertTrue; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Verifier; import org.junit.runner.JUnitCore; public class VerifierTest { public static void main(String[] args) { JUnitCore.main(VerifierTest.class.getName()); } private boolean success; @Rule public Verifier verifier = new Verifier() { @Override public void verify() { assertTrue(success); } }; @Test public void testSuccessCase() { success = true; System.out.println("I am testSuccessCase() method."); } @Test public void testFailureCase() { success = false; System.out.println("I am testFailureCase() method."); } }
実行結果(スタックトレースが長いので適宜省略します。)
$ java tryjunit4.rule.VerifierTest JUnit version 4.11 .I am testSuccessCase() method. .I am testFailureCase() method. E Time: 0.006 There was 1 failure: 1) testFailureCase(tryjunit4.rule.VerifierTest) java.lang.AssertionError at org.junit.Assert.fail(Assert.java:86) at org.junit.Assert.assertTrue(Assert.java:41) at org.junit.Assert.assertTrue(Assert.java:52) at tryjunit4.rule.VerifierTest$1.verify(VerifierTest.java:21) ーー省略ーー at tryjunit4.rule.VerifierTest.main(VerifierTest.java:12) FAILURES!!! Tests run: 2, Failures: 1 $
テストメソッドを実行後、verify()メソッドが実行されます。(21行目)テスト成功ならそのまま終了し、失敗なら AssertionErrorをスローします。
今までのRuleは、テストメソッド単位に共通処理を実行していました。 ClassRuleではテストメソッド実行単位でなくテストクラスの単位での共通処理を管理できます。
package tryjunit4.rule; import org.junit.ClassRule; import org.junit.Test; import org.junit.rules.ExternalResource; import org.junit.runner.JUnitCore; public class ClassRuleTest { public static void main(String[] args) { JUnitCore.main(ClassRuleTest.class.getName()); } @ClassRule public static ExternalResource externalResource = new ExternalResource() { @Override protected void before() { System.out.println("I am before() method."); }; @Override protected void after() { System.out.println("I am after() method."); }; }; @Test public void testNantoka() { System.out.println("I am testNantoka() method."); } @Test public void testKantoka() { System.out.println("I am testKantoka() method."); } @Test public void testDousita() { System.out.println("I am testDousita() method."); } }
実行結果
$ java tryjunit4.rule.ClassRuleTest JUnit version 4.11 I am before() method. .I am testKantoka() method. .I am testNantoka() method. .I am testDousita() method. I am after() method. Time: 0.012 OK (3 tests) $
ClassRule をexternalResourceフィールドに適用しこれをstaticにします。すると、 @BeforeClass,@Before,@AfterClass,@Afterそれぞれの違いを検証する。で検証した@BeforeClass と@AfterClassと同様の挙動になります。 主にExternalResourceを使って、たとえばDB接続の開始と終了の処理を行う使い方になるでしょう。
今まで出した例ではRuleは1クラスに1個だけでした。実際は複数設定出来ます。この場合、Ruleはどういう順序で実行されるでしょうか? RuleのAPIリファレンス によれば、「Ruleが付与されているフィールド・メソッドが複数あった場合、 実行される順序はJavaVMのリフレクションAPIの実装に依存する。」とのことです。 つまり、ソースコード上のフィールドの順序は実際の実行順序と全く関係がないということです。 ではどうするか?実行されるRuleの順序を決定するにはRuleChainを使います。
package tryjunit4.rule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.JUnitCore; public class RuleChainTest { public static void main(String[] args) { JUnitCore.main(RuleChainTest.class.getName()); } @Rule public TestRule chain = RuleChain .outerRule(new LoggingRule("outer rule")) .around(new LoggingRule("middle rule")) .around(new LoggingRule("inner rule")); @Rule public LoggingRule log1 = new LoggingRule("log1"); @Rule public LoggingRule log2 = new LoggingRule("log2"); @Rule public LoggingRule log3 = new LoggingRule("log3"); @Test public void testNantoka() { } }
package tryjunit4.rule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; public class LoggingRule extends TestWatcher { String message; LoggingRule(String message) { this.message = message; } @Override protected void starting(Description d) { System.out.printf("starting %s %n", message); } @Override protected void finished(Description d) { System.out.printf("finished %s %n", message); } }
実行結果
$ java tryjunit4.rule.RuleChainTest JUnit version 4.11 .starting log3 starting log2 starting log1 starting outer rule starting middle rule starting inner rule finished inner rule finished middle rule finished outer rule finished log1 finished log2 finished log3 Time: 0.007 OK (1 test) $
15行目のRuleChainのメソッド呼び出しで、LoggingRuleを組みあわせて順序を決定しています。 20~25行目でもRuleを設定しています。フィールド定義の逆順のようですが、これはたまたまMacのJavaVMがそうだったというだけのことです。 別のJavaVMでは違った順序になるかもしれません。
この文書は
表示 2.1 日本 (CC BY 2.1)
によってライセンスされます。