Junit Best Practices for Approving Complex Objects

I am currently writing many JUnit tests for an outdated system.

Often I turn to the question: What is the best way to approve 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 easy way to test every step of such a parser, and I cannot archive it so much, as this is legacy code ...

Thank you for your help!

+5
source share
4 answers

As a sisyphus, I would suggest using hamcrest matchers.

But I recommend programming a custom layout . Next line

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

might look like this:

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

You will need a custom class class ResultMatcher , which should have the following form:

 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 several advantages to this design:

  • You do not need the equals-method to be defined
  • you can define default values ​​for each attribute so that you can only test the attributes that interest you and match the others by default.
  • The test does not have to check every attribute of the object (this does it)
+3
source

I would give assertj extracting a try function, for example:

 // 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")); 

An example is described in detail here: http://joel-costigliola.imtqy.com/assertj/assertj-core-features-highlight.html#extracted-properties-assertion

You can also use a specific comparison strategy to compare actual and expected results, and finally field support by field comparison: isEqualToComparingFieldByField, isEqualToComparingOnlyGivenFields and isEqualToIgnoringGivenFields.

Hope this helps

+3
source

Instead of a long method signature, I would do something like. (pseudocode, disclaimer if 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); 
0
source

You might want to take a look at hamcrest Mappings in conjunction with the assertThat() method from junit. You might find that your test code is a bit like

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

Alternatively, assertj , which allows you to write helper classes that freely pass several separate statements about the same object, using the assertJ version of assertThat , rather than JUnit. This will make your test code look a bit more like

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

Source: https://habr.com/ru/post/1239496/


All Articles