6

完全なテスト スイートを実行しているときに、テストの失敗の原因となった例外が (SLF4J-) ログに表示されると便利です。これを達成するための最良の方法は何ですか?

私が欲しいもの

私のために例外ロギングを処理するjunit4ルールです。コード

@Rule
public TestRule logException = new TestWatcher() {
    @Override
    public void failed(Description d) {
        catch (Exception e) {
            logger.error("Test ({}) failed because of exception {}", d, e);
            throw e;
        }
    }
}

もちろん、try ブロックからしか例外をキャッチできないため、機能しません。同様に単純で一般的な方法で何らかの方法でこれを達成するための回避策はありますか?


ところで、私が今していること

作成された瞬間に例外をログに記録しています。しかし、呼び出し元とライブラリの間のインターフェースで例外をログに記録する方が良いので、私の場合はテストケースで. 例外が作成されたときにログに記録しないと、呼び出し元がそれらをログに記録することを決定したときに、それらが複数回表示されないことも保証されます。

4

3 に答える 3

14

TestRule、特に apply() を拡張する必要があります。例として、org.junit.rules.ExternalResource & org.junit.rules.TemporaryFolder を見てください。

ExternalResource は次のようになります。

public abstract class ExternalResource implements TestRule {
    public Statement apply(Statement base, Description description) {
        return statement(base);
    }

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } finally {
                    after();
                }
            }
        };
    }

    /**
     * Override to set up your specific external resource.
     * @throws if setup fails (which will disable {@code after}
     */
    protected void before() throws Throwable {
        // do nothing
    }

    /**
     * Override to tear down your specific external resource.
     */
    protected void after() {
        // do nothing
    }
}

次に、TemporaryFolder はこれを拡張し、before() と after() を実装します。

public class TemporaryFolder extends ExternalResource {
    private File folder;

    @Override
    protected void before() throws Throwable {
        // create the folder
    }

    @Override
    protected void after() {
        // delete the folder
    }

したがって、before は testMethod の前に呼び出され、after は finally で呼び出されますが、次のように例外をキャッチしてログに記録できます。

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } catch (Exception e) {
                    log.error("caught Exception", e);
                } finally {
                    after();
                }
            }
        };
    }

編集:次の作品:

public class SoTest {
    public class ExceptionLoggingRule implements TestRule {
        public Statement apply(Statement base, Description description) {
            return statement(base);
        }

        private Statement statement(final Statement base) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    try {
                        base.evaluate();
                    } catch (Exception e) {
                        System.out.println("caught an exception");
                        e.printStackTrace(System.out);
                        throw e;
                    }
                }
            };
        }
    }

    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
    @Rule public ExpectedException expectedException = ExpectedException.none();

    @Test
    public void testMe() throws Exception {
        expectedException.expect(IOException.class);
        throw new IOException("here we are");
    }
}

テストに合格し、次の出力が得られます。

caught an exception
java.io.IOException: here we are
    at uk.co.farwell.junit.SoTest.testMe(SoTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
...

ルールが適用される順序は、testMe メソッドを呼び出す ExceptionLoggingRule を呼び出す ExpectedException です。ExceptionLoggingRule は例外をキャッチし、ログに記録して再スローし、ExpectedException によって処理されます。

予期しない例外のみをログに記録する場合は、ルールの宣言順序を切り替えるだけです。

    @Rule public ExpectedException expectedException = ExpectedException.none();
    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();

このようにして、expectedException が最初に適用され (つまり、exceptionLoggingRule にネストされ)、予期されていない例外のみが再スローされます。さらに、何らかの例外が予想され、何も発生しなかった場合、expectedException は AssertionError をスローし、これもログに記録されます。

この評価順序は保証されていませんが、非常に異なる JVM で遊んだり、Test クラス間で継承したりしない限り、変化する可能性はほとんどありません。

評価順序が重要な場合は、いつでも一方のルールを他方のルールに渡して評価することができます。

編集: 最近リリースされた Junit 4.10 では、 @RuleChain を使用してルールを正しくチェーンできます。

public static class UseRuleChain {
   @Rule
   public TestRule chain= RuleChain
                          .outerRule(new LoggingRule("outer rule")
                          .around(new LoggingRule("middle rule")
                          .around(new LoggingRule("inner rule");

   @Test
   public void example() {
           assertTrue(true);
   }
}

ログを書き込みます

starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule
于 2011-09-21T17:30:09.420 に答える
2

箱の外を見てください... テストを実行するために何を使用していますか? テストを実行するためのほとんどの環境 (Ant、Jenkins、Maven など) には、XML ファイルを出力するテストランナーを使用する手段があり、スイートからの XML ファイルを包括的なレポートに集約する機能がサポートされています。

于 2011-09-21T17:30:00.713 に答える
0

これはとても簡単に思えるので、私は間違っていると思います。あなたは別のことを尋ねていますが、おそらく私は助けることができます:

JUnit 4.X

@Test(expected=Exception.class)

テストで例外がスローされた場合はテストに合格するか、Junit フレームワークによってキャプチャされたメッセージで失敗します

于 2011-10-05T21:53:20.033 に答える