4

I'm testing a WeekConverter for Xalan use and wondering what is my test exactly doing. :D

Having the following test method:

@Test(expected = IllegalArgumentException.class)
  public void testConvertTwoDigitYearWithWrongInput() {
  WeekConverter weekConverter = new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR);

  //wrong or empty inputs
  assertEquals("0", weekConverter.convert(""));
  assertEquals("0", weekConverter.convert("abcdefgh"));
}

Will this test expect an exception for all asserts, or only for the first assert? If only the first, which would mean that I have to create a test method for each assert, although I'm expecting the same exception in both cases. Can someone confirm my example here, please?

I also have a test for null, which yields a NullPointerException. The soft validation is the following:

if (inputDate == null) {
  do something and throw NullPointerexception
} else if (inputDate.isEmpty()) {
  do something and throw IllegalArgumentException, since inputDate is not really null
} else if (inputDate.matches(regex)) {
  go futher and convert
} else {
  do something and throw IllegalArgumentException, since inputDate does not match regex
}

Therefore the one test method expecting IllegalArgumentException with two asserts. But it's obvious that I need two different test methods, not only to respect functionality of JUnit , but also that I expect a throw from two different states.

informatik01
  • 16,038
  • 10
  • 74
  • 104
Ed Michel
  • 898
  • 1
  • 11
  • 23

5 Answers5

3

You can break your method into multiple methods, but if you have many input samples it would be inconvenient.

You can use the following approach instead:

@Test
public void testConvertTwoDigitYearWithWrongInput() {
    WeekConverter weekConverter = new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR); 

    assertFailsToConvert(weekConverter, ""); 
    assertFailsToConvert(weekConverter, "abcdefgh");
}

private void assertFailsToConvert(WeekConverter weekConverter, String input) {
    try {
        weekConverter.convert(input);
        fail("Should not convert [" + input + "]");
    } catch (IllegalArgumentException ex) {}
}
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • The problem with this is that the tests are no longer self-documenting without looking at the test, and can no longer be documented in human-readable form without an AST and guesswork. Keeping them separate allows far better info generation. – Dave Newton Dec 20 '11 at 17:02
  • 1
    But having a large number of separate tests for different input samples would hamper readability and maintainability, so that you should decide what is more important for you. – axtavt Dec 20 '11 at 17:10
1

Try catch-exception:

@Test
public void testConvertTwoDigitYearWithWrongInput() {

    WeekConverter weekConverter = ...

    // wrong or empty inputs
    verifyException(weekConverter, IllegalArgumentException.class)
       .convert("");
    verifyException(weekConverter, IllegalArgumentException.class)
       .convert("abcdefgh");
}
rwitzel
  • 1,694
  • 17
  • 21
1

You should provide multiple test methods, since they're testing different things.

The exception will be thrown the first time the converter gets an illegal argument.

You should also test a null input, just to document behavior.

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
1

The test is just expecting that IllegalArgumentException is being throw, regardless from where or why it is being throwed.

I recommend you to split it in two tests.

1

You can put the creation of the conversion fixture in a separate @Before setup method, and then you can have (three) separate test cases for dealing with null, "", and "abcdef".

If there are more cases to test, a neat way in JUnit is to use the @Parameters annotation and corresponding runner.

Your test class would deal with incorrect two digit years only. Its constructor would be parameterized with an inputDate of type String.

The static method yielding the @Parameters would return a collection containing "" and abcdefg (and other funny cases).

The single test case would expect an IllegalArgumentException.

@RunWith(Parameterized.class)
public class IncorrectTwoDigitYears {
    String inputDate;

    public IncorrectTwoDigitYears(String inputDate) {
        this.inputDate = inputDate;
    }

    @Test(expected = IllegalArgumentException.class)
    public void testFormat() {
        (new WeekConverter(WeekConverter.Strategy.TWO_DIGIT_YEAR))
            .convert(inputDate);
    }

    @Parameters
    public static Collection<Object[]> data() {
       Object[][] data = new Object[][] { 
           { "" }, { "abcdef" }, { "0" }, { "000" }, { "##" } };
       return Arrays.asList(data);
    }
}

The pay off will be higher if you have more than just two cases to test.

avandeursen
  • 8,458
  • 3
  • 41
  • 51