2

I am implementing a validator for some userdata.

Unfortunately the userdata contains of a total of 37 fields which makes the test class quite monstrous.

Most of the fields contain data with similar requirements (i.e. checking for length constraints).

Excerpt of the code:

public class Userdata {

    private String            firstName;
    private String            lastName;
    private String            birthName;
    private String            birthPlace;
    private String            city;
    private String            street;
    private String            zipCode;
    private String            country;
    private String            citizenship;

    // getters/setters
}

and (parts of) the test class:

public class UserdataValidatorTest {
    UserdataValidator cut = new UserdataValidator();

    @Test
    public void firstNameMaxPermittedLength()
    {
        Userdata userdata = minimumRequirements();
        userdata.setFirstName( utf8Chars( 64 ) );

        assertNoViolations( cut.validate( userdata ) );
    }

    @Test
    public void firstNameExceedsPermittedLength()
    {
        Userdata userdata = minimumRequirements();
        userdata.setFirstName( utf8Chars( 65 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void lastNameMaxPermittedLength()
    {
        Userdata userdata = minimumRequirements();
        userdata.setLastName( utf8Chars( 64 ) );

        assertNoViolations( cut.validate( userdata ) );
    }

    @Test
    public void lastNameExceedsPermittedLength()
    {
        Userdata userdata = minimumRequirements();
        userdata.setLastName( utf8Chars( 65 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void citizenshipIllegalChars()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCitizenship( "2E" );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void citizenshipLegal()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCitizenship( iso3166Country() );

        assertNoViolations( cut.validate( userdata ) );
    }

    @Test
    public void citizenshipTooLong()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCitizenship( alphabetic( 3 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void citizenshipTooShort()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCitizenship( alphabetic( 1 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void countryIllegalChars()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCountry( "2E" );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void countryLegal()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCountry( iso3166Country() );

        assertNoViolations( cut.validate( userdata ) );
    }

    @Test
    public void countryTooLong()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCountry( alphabetic( 3 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    @Test
    public void countryTooShort()
    {
        Userdata userdata = minimumRequirements();
        userdata.setCountry( alphabetic( 1 ) );

        assertSingleViolation( cut.validate( userdata ) );
    }

    // ... more tests
}

Is there a pleasant way to generate the test cases? I still want to have i.e. 4 tests for each country.

Edit

There are a number of complex (dependent on each other) fields, which I have omitted here, which is a reason why I have decided to not use the Bean Validation API.

Max Fichtelmann
  • 3,366
  • 1
  • 22
  • 27
  • What about throwing lots of random data (with a constant seed; see `Random(int seed)`) in a loop at those validators?. Generated data + loops = not so verbose. – tucuxi Jan 07 '14 at 11:06
  • 1
    See also http://stackoverflow.com/questions/6453235/what-does-damp-not-dry-mean-when-talking-about-unit-tests – Raedwald Jan 07 '14 at 13:21

2 Answers2

2

IMHO Unit tests are not production code and don't have to be held up the same standard. You can easy refactor and delete test code without direct impact on production. For me, DRY make sense for production code, but WET (Write Everything Twice) also works in unit test code.

To directly answer your question, you could use a loop, and you could combine the tests, but I suspect they don't help much.

e.g.

@Test
public void firstNameMaxPermittedLength() {
    Userdata userdata = minimumRequirements();
    userdata.setFirstName(utf8Chars(64));
    assertNoViolations(cut.validate(userdata));

    userdata.setFirstName(utf8Chars(65));
    assertSingleViolation(cut.validate(userdata));
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • combining the tests would IMO do more harm, because in case of failures, test cases would not be executed. Generally I agree with WET for test code - but in this case my test class is halfway finished and already close to 600 LoC. Plus I fear to have forgotten some check and having to selectively implement that for ~10 fields is quite prone to human error. – Max Fichtelmann Jan 07 '14 at 11:53
  • 1
    The target should be no failed tests. Whether one test fails instead of two tests failing, it doesn't make much difference really. The only way I can see of generating the tests using reflection or data driven tests, but this tends to combine more tests cases. – Peter Lawrey Jan 07 '14 at 12:29
  • 1
    I guess you are right. So I would refactor to one method per field, calling something like `assertProperStringLengthValidation(cut, "firstName", 64);` – Max Fichtelmann Jan 07 '14 at 12:43
  • Yes, the test could check that 64 is ok but 64+1 is not. You could have the same line for lastName and any other String which has a limited length. – Peter Lawrey Jan 07 '14 at 12:59
0

If all these fields do the same thing and the names are consistent, why not just ask the class for a list of its methods, loop through all the ones that start with set, and do whatever it is you need to do?

Pete Verdon
  • 335
  • 1
  • 10
  • Unfortunately they do not do exactly the same - there are several country validations which are equal and several length validations which do not have the same length restrictions – Max Fichtelmann Jan 07 '14 at 11:47
  • 1
    OK. If the required behaviour is different then it seems unavoidable that the tests should be different. For what it's worth, in a similar situation I created a set of annotations that defined validity for a field, then had the validator look at those rather than hard-code for each field. Then you just need to test the validator against the various annotations. Since then, Java has built-in something similar: http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html , though I've not used it myself yet. Would this do what you need and eliminate the need for testing your own validation at all? – Pete Verdon Jan 07 '14 at 12:02
  • Thank you very much for the suggestion. I have worked with the Validation API before, but I failed to add in my question, that I have a number of fields, that are dependent from other fields, which is afaik not trivial to implement using the validation API. – Max Fichtelmann Jan 07 '14 at 12:16