I am writing a lot JUnit tests these days for a legacy system.
我正在为遗留系统编写很多JUnit测试。
Often I come to the question: What is the best way to assert complex Objects?
我常常提出这样的问题:断言复杂对象的最佳方法是什么?
Here is my current code
这是我目前的代码
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
}
}
There is no simple way to test each step of such a parser and I can't cahnge that much since it is legacy code...
没有简单的方法可以测试这样一个解析器的每一步,因为它是遗留代码,所以我无法改变这一点。
Thanks for your Help!
谢谢你的帮助!
4 个解决方案
#1
4
As sisyphus I would suggest using hamcrest matchers.
作为西西弗斯,我建议使用hamcrest matchers。
But I recommend to program a custom matcher. Following line
但我建议编写一个自定义匹配器。以下行
assertResult(listOfResults.get(0), "20151223", 2411189L, isEmptyOrNullString(), "2.71", "16.99");
may then look like:
可能看起来像:
assertThat(listOfResults, contains(
ResultMatcher.matchesResult().withFirstAttribute("20151223").andSecondAttribute(2411189L)...
... // here the matchers for the other elements of the list
));
You will need the custom matcher class ResultMatcher
which should have following form:
您将需要自定义匹配器类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;
}
}
There are some Advantages in this design:
这个设计有一些优点:
- you do not need an equals-method to be defined
- you can define defaults for every attribute, so you can test only attributes you are interested in and match others by default
- the test not have to check each attribute of the object (the matcher does this)
你不需要定义一个equals方法
您可以为每个属性定义默认值,因此您可以仅测试您感兴趣的属性并默认匹配其他属性
测试不必检查对象的每个属性(匹配器执行此操作)
#2
3
I would give assertj extracting feature a try, example:
我会尝试使用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"));
The example is described in detail here : http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#extracted-properties-assertion
这个例子在这里有详细描述:http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#extracted-properties-assertion
You can also use specific comparison strategy to compare actual and expected results, lastly there is support for field by field comparison : isEqualToComparingFieldByField, isEqualToComparingOnlyGivenFields and isEqualToIgnoringGivenFields.
您还可以使用特定的比较策略来比较实际和预期的结果,最后支持逐字段比较:isEqualToComparingFieldByField,isEqualToComparingOnlyGivenFields和isEqualToIgnoringGivenFields。
Hope it can help
希望它可以提供帮助
#3
0
Instead of a long method signature, I would do something like. (pseudo code, disclaimer in case it does not compile):
而不是长方法签名,我会做类似的事情。 (伪代码,如果不编译则免责声明):
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));
}
}
and then I could use it like this:
然后我可以像这样使用它:
new AssertResult().setXXX(123).setYYY("asdasd").check(result);
#4
-1
You might want to take a look at hamcrest Matchers in conjunction with the assertThat()
method from junit. You might find your test code looking a bit more like
你可能想看看hamcrest Matchers和junit中的assertThat()方法。您可能会发现您的测试代码看起来更像
assertThat(listOfResults.get(0), equalTo(expectedObject));
Alternatively, there's assertj which allows you to write helper classes which chain multiple separate assertions about the same object in a fluent manner, using the assertJ version of assertThat
rather than JUnit's. That would allow your test code to look a bit more like
或者,有一个assertj允许你编写一个帮助类,它使用assertThat而不是JUnit的assertJ版本,以流畅的方式链接关于同一个对象的多个单独的断言。这将使您的测试代码看起来更像
assertThat(listOfResults.get(0)).hasXXX("someString").hasYYY(1.234)
#1
4
As sisyphus I would suggest using hamcrest matchers.
作为西西弗斯,我建议使用hamcrest matchers。
But I recommend to program a custom matcher. Following line
但我建议编写一个自定义匹配器。以下行
assertResult(listOfResults.get(0), "20151223", 2411189L, isEmptyOrNullString(), "2.71", "16.99");
may then look like:
可能看起来像:
assertThat(listOfResults, contains(
ResultMatcher.matchesResult().withFirstAttribute("20151223").andSecondAttribute(2411189L)...
... // here the matchers for the other elements of the list
));
You will need the custom matcher class ResultMatcher
which should have following form:
您将需要自定义匹配器类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;
}
}
There are some Advantages in this design:
这个设计有一些优点:
- you do not need an equals-method to be defined
- you can define defaults for every attribute, so you can test only attributes you are interested in and match others by default
- the test not have to check each attribute of the object (the matcher does this)
你不需要定义一个equals方法
您可以为每个属性定义默认值,因此您可以仅测试您感兴趣的属性并默认匹配其他属性
测试不必检查对象的每个属性(匹配器执行此操作)
#2
3
I would give assertj extracting feature a try, example:
我会尝试使用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"));
The example is described in detail here : http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#extracted-properties-assertion
这个例子在这里有详细描述:http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#extracted-properties-assertion
You can also use specific comparison strategy to compare actual and expected results, lastly there is support for field by field comparison : isEqualToComparingFieldByField, isEqualToComparingOnlyGivenFields and isEqualToIgnoringGivenFields.
您还可以使用特定的比较策略来比较实际和预期的结果,最后支持逐字段比较:isEqualToComparingFieldByField,isEqualToComparingOnlyGivenFields和isEqualToIgnoringGivenFields。
Hope it can help
希望它可以提供帮助
#3
0
Instead of a long method signature, I would do something like. (pseudo code, disclaimer in case it does not compile):
而不是长方法签名,我会做类似的事情。 (伪代码,如果不编译则免责声明):
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));
}
}
and then I could use it like this:
然后我可以像这样使用它:
new AssertResult().setXXX(123).setYYY("asdasd").check(result);
#4
-1
You might want to take a look at hamcrest Matchers in conjunction with the assertThat()
method from junit. You might find your test code looking a bit more like
你可能想看看hamcrest Matchers和junit中的assertThat()方法。您可能会发现您的测试代码看起来更像
assertThat(listOfResults.get(0), equalTo(expectedObject));
Alternatively, there's assertj which allows you to write helper classes which chain multiple separate assertions about the same object in a fluent manner, using the assertJ version of assertThat
rather than JUnit's. That would allow your test code to look a bit more like
或者,有一个assertj允许你编写一个帮助类,它使用assertThat而不是JUnit的assertJ版本,以流畅的方式链接关于同一个对象的多个单独的断言。这将使您的测试代码看起来更像
assertThat(listOfResults.get(0)).hasXXX("someString").hasYYY(1.234)