断言复杂对象时的Junit最佳实践

时间:2015-12-30 14:19:15

标签: java junit hamcrest

我正在为遗留系统编写很多JUnit测试。

我常常提出这样的问题:断言复杂对象的最佳方式是什么?

这是我目前的代码

public class SomeParserTest {

    @Test
    public void testParse() throws Exception {
        final SomeParser someParser = new SomeParser();
        someParser.parse("string from some file");

        final List<Result> listOfResults = someParser.getResults();
        assertThat(listOfResults, hasSize(5));

        assertResult(listOfResults.get(0), "20151223", 2411189L, isEmptyOrNullString(), "2.71", "16.99");
        assertResult(listOfResults.get(1), "20151229", 2411190L, isEmptyOrNullString(), "2.86", "17.9");
        assertResult(listOfResults.get(2), "20151229", 2411191L, is("1.26"), ".75", "23.95");
        assertResult(listOfResults.get(3), "20151229", 2411192L, is("2.52"), "1.5", "47.9");
        assertResult(listOfResults.get(4), "20151229", 2411193L, isEmptyOrNullString(), "2.71", "16.99");

        final List<SubResult> listofSubResuls = someParser.getSubResultOf(listOfResults.get(0));
        assertThat(listofSubResuls, hasSize(1));
        assertSubResult(listofSubResuls.get(0), 12.5D, "20151223", 1L, 14.87D, 16.99D, 0L, null, 67152L, "20151223", "2", 0L, "02411189", 56744349L);

        final List<SubResult> listofSubResuls1 = someParser.getListofBBBS(listOfResults.get(1));
        assertThat(listofSubResuls1, hasSize(2));
        assertSubResult(listofSubResuls1.get(0), 30.0D, "20151228", 1L, 12.53D, 17.9D, 0L, null, 67156L, "20151229", "2", 0L, "02411190", 56777888L);
        assertSubResult(listofSubResuls1.get(1), 33.3D, "20151228", 1L, 4.66D, 6.99D, 1L, "J", 67156L, "20151229", "2", 21L, "02411190", 56777889L);
//And 50 Lines more
    }

//  how to avoid so many parameters?
    private void assertSubResult(final SubResult subResult, final double someDouble, final String bestellDatum,
            final long someLong, final double someDouble2, final double someDouble3, final long someLong3,
            final String someString,
            final long someLong1,
            final String someString4, final String someString3, final long someLong4, final String rechnungsNummer,
            final long someLong2) {
        assertThat(subResult.getXXX(), is(nullValue()));
        assertThat(subResult.getXYX().getTag(), is(someDouble2));
        assertThat(subResult.getXYX(), is("some constant"));
//      and much more
    }

    //  how to avoid so many parameters?
    private void assertResult(final Result result, final String string1234, final long abc,
            final String string1, final String string12, final String string134) {
        assertThat(result.getXXX(), is(nullValue()));
        assertThat(result.getXYX().getTag(), is(someDouble2));
        assertThat(result.getXYX(), is("some constant"));
//      and much more
    }
}

没有简单的方法来测试这样一个解析器的每一步,因为它是遗留代码,所以我不能改变那么多......

感谢您的帮助!

4 个答案:

答案 0 :(得分:4)

作为西西弗斯,我建议使用hamcrest matchers。

但我建议编制一个custom matcher。以下行

assertResult(listOfResults.get(0), "20151223", 2411189L, isEmptyOrNullString(), "2.71", "16.99");

可能看起来像:

assertThat(listOfResults, contains(
  ResultMatcher.matchesResult().withFirstAttribute("20151223").andSecondAttribute(2411189L)...
  ... // here the matchers for the other elements of the list
));

您需要具有以下格式的自定义匹配器类ResultMatcher

class ResultMatcher extends TypeSafeMatcher<Result> {
   Matcher<String> firstAttribute = Matchers.any(String.class);
   Matcher<String> secondAttribute = Matchers.any(String.class);
   ...

   ResultMatcher withFirstAttribute(String firstAttribute) {
     this.firstAttribute = Matchers.equalTo(firstAttribute);
     return this;
   }

   ...

   public boolean matchesSafely(Result result) {
     if (!firstAttribute.matches(result.getFirstAttribute())) {
       return false
     }
     ...
     return true;
   }

}

此设计有一些优点:

  • 您不需要定义等号方法
  • 您可以为每个属性定义默认值,因此您只能测试您感兴趣的属性并默认匹配其他属性
  • 测试不必检查对象的每个属性(匹配器执行此操作)

答案 1 :(得分:3)

我会尝试使用assertj提取功能,例如:

// fellowshipOfTheRing is a List<TolkienCharacter>
assertThat(fellowshipOfTheRing).extracting("name", "age", "race.name")
                               .contains(tuple("Boromir", 37, "Man"),
                                         tuple("Sam", 38, "Hobbit"),
                                         tuple("Legolas", 1000, "Elf"));

此处详细介绍了示例:http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#extracted-properties-assertion

您还可以使用特定的比较策略来比较实际和预期的结果,最后支持逐字段比较:isEqualToComparingFieldByField,isEqualToComparingOnlyGivenFields和isEqualToIgnoringGivenFields。

希望它可以提供帮助

答案 2 :(得分:0)

而不是长方法签名,我会做类似的事情。 (伪代码,如果不编译则免责声明):

class AssertResult {
    private int xxx;
    private String yyy;
    public AssertResult setXXX(int xxx) {
        this.xxx = xxx;
        return this;
    }
    public AssertResult setYYY(String yyy) {
        this.yyy = yyy;
        return this;
    }
    public void check(Result result) {
        assertThat(result.getXXX(), is(xxx));
        assertThat(result.getYYY(), is(yyy));
    }
 }

然后我可以像这样使用它:

new AssertResult().setXXX(123).setYYY("asdasd").check(result);

答案 3 :(得分:-1)

您可能希望结合使用junit中的assertThat()方法查看hamcrest Matchers。您可能会发现您的测试代码看起来更像

assertThat(listOfResults.get(0), equalTo(expectedObject));

或者,有assertj允许你编写辅助类,它使用assertThat的assertJ版本而不是JUnit来以流畅的方式链接关于同一对象的多个单独的断言。这将使您的测试代码看起来更像

assertThat(listOfResults.get(0)).hasXXX("someString").hasYYY(1.234)