2

私は、errorCode を埋め込んだ多くの「BusinessException」を含むプロジェクトに取り組んでいます。

例外のすべての単体テストでは、次のようなパターンを繰り返すエラー コードをテストする必要があります。

@Test
public void zipFileReaderCtorShouldThrowAnExceptionWithInexistingArchive() {
    try {
        zfr = new ZipFileReader("unexpected/path/to/file");
        fail("'BusinessZipException' not throwed");
    } catch (BusinessZipException e) {
        assertThat("Unexpected error code", e.getErrorCode(), is(ErrorCode.FILE_NOT_FOUND));
    } catch (Exception e) {
        fail("Unexpected Exception: '" + e + "', expected: 'BusinessZipException'");
    }
}

(エラーコードのテストのため、JUnit アノテーションの使用は不可能です)

特に、fail() のエラー メッセージで例外名をコピー/貼り付けする必要があったため、これを行うのは退屈でした。

そこで、Util クラスを作成しました。抽象クラスを使用して、例外アサート テストを処理します。

パブリック抽象クラス TestExceptionUtil {
    public void runAndExpectException(Class expectedException, String expectedErrorCode) {
        String failUnexpectedExceptionMessage = "予期しない例外です。予期されるのは: '%s' ですが、取得したのは: '%s'";
        試す {
            codeToExecute();
            fail("'" + expectedException.getName() + "' スローされません");
        } キャッチ (BusinessException e) {
            if (e.getClass().equals(expectedException)) {
                assertThat("例外エラー コードは予期されません", e.getErrorCode(), is(expectedErrorCode));
            } そうしないと {
                fail(String.format(failUnexpectedExceptionMessage, expectedException.getName(), e));
            }
        キャッチ(例外e){
            fail(String.format(failUnexpectedExceptionMessage, expectedException.getName(), e));
        }
    }

    abstract public void codeToExecute();
}

次に、クライアントは次のように使用します。

@Test
public void zipFileReaderCtorShouldThrowAnExceptionWithInexistingArchive() {
    new TestExceptionUtil() {
        @Override
        public void codeToExecute() {
            zfr = new ZipFileReader("unexpected/path/to/file");
        }
    }.runAndExpectException(BusinessTechnicalException.class, ErrorCode.FILE_NOT_FOUND);
}

「きれい」だと思いますか?改善できると思いますか?重すぎる、または役に立たないと思いますか?私の主な目的は、開発チームでテスト例外を統一することです。(そしてもちろん因数分解コード)

読んでくれてありがとう!

4

2 に答える 2

10

JUnit はどうですか?ExpectedException Rule

Rule最初に、テスト クラスの先頭でを宣言します。

@Rule
public final ExpectedException ee = ExpectedException.none();

次に、テストメソッドで、次を期待できることを宣言できますException

@Test
public void testStuff() {
    ee.expect(IllegalArgumentException.class);
    ee.expectMessage("My Exception text");
}

これはあなたのアプローチよりもかなりきれいだと思います。

次に、 hamcrest を使用してメッセージMatcherを一致させることができます。Exception

@Test
public void testStuff() {
    ee.expect(IllegalArgumentException.class);
    ee.expectMessage(containsString("error"));
    ee.expect(hasProperty("errorCode", is(7)));
}

は、名前付きプロパティのhasProperty Matchergetter を探し、それが 2 番目の引数 (別の です) と一致することを確認しますMatcher

独自の を実装することもできます。Matcherその場合、hamcrest への依存は必要ありません。

public class ErrorCodeMatcher extends BaseMatcher<Throwable> {

    private final int expectedErrorCode;

    public ErrorCodeMatcher(int expectedErrorCode) {
        this.expectedErrorCode = expectedErrorCode;
    }

    @Override
    public boolean matches(Object o) {
        return ((BusinessZipException) o).getErrorCode() == expectedErrorCode;
    }

    @Override
    public void describeTo(Description d) {
        d.appendText("Expected error code was" + expectedErrorCode);
    }
}

これは次のように使用されます。

ee.expect(new ErrorCodeMatcher(7));

staticファクトリ メソッドとインポートを使用すると、staticこれは非常にクリーンになります。

ee.expect(exceptionWithErrorCode(7));

メソッドを使用してビジネスをinterface定義するコモンがある場合、たとえば呼び出された場合、クラスを拡張して少しきれいなコードを作成できます。ExceptiongetErrorCode()ErrorAwareExceptionTypeSafeMatcher<T>

public class ErrorCodeMatcher<T extends Exception & ErrorAwareException> extends TypeSafeMatcher<T> {

    public static <E extends Exception & ErrorAwareException> ErrorCodeMatcher<E> exceptionWithErrorCode(final int expectedErrorCode) {
        return new ErrorCodeMatcher<E>(expectedErrorCode);
    }
    private final int expectedErrorCode;

    public ErrorCodeMatcher(int expectedErrorCode) {
        this.expectedErrorCode = expectedErrorCode;
    }

    @Override
    protected boolean matchesSafely(final T t) {
        return t.getErrorCode() == expectedErrorCode;
    }

    @Override
    public void describeTo(Description d) {
        d.appendText("Expected error code was" + expectedErrorCode);
    }
}

hamcrest を使用することを選択した場合は、プロジェクトにjunit-dep純粋ではなく必ず含めるようjunitにしてください。そうしないと、hamcrest クラスが に含まれる hamcrest クラスと衝突しjunitます。Maven では、これは次のようになります。

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-all</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit-dep</artifactId>
    <version>4.11</version>
    <scope>test</scope> 
</dependency>
于 2013-10-02T21:03:18.590 に答える
3

ここで実際に車輪を再発明していると思います。スローされた特定の例外のテストメソッドを成功させるアノテーションのいずれかexpectedのパラメーターを使用できます。@Testまたは、ExpectedExceptionルールを使用して基本的に同じことを行いますが、より多くの機能があります。どちらかを試してください

@Test(expected = Exception.class)
public void myTest() {
    throw new Exception();
}

また

@Rule
private ExpectedException rule = ExpectedException.none();

@Test
public void myTest() {
    rule.expect(Exception.class);
    throw new Exception();
}
于 2013-10-02T21:08:56.670 に答える