Bad form for JUnit test to throw exception?

I'm pretty new to JUnit, and I don't really know what best practices are for exceptions and exception handling.

For example, let's say I'm writing tests for an IPAddress class. It has a constructor IPAddress(String addr) that will throw an InvalidIPAddressException if addr is null. As far as I can tell from googling around, the test for the null parameter will look like this.

@Test
public void testNullParameter()
{
    try
    {
        IPAddress addr = new IPAddress(null);
        assertTrue(addr.getOctets() == null);
    }
    catch(InvalidIPAddressException e)
    {
        return;
    }

    fail("InvalidIPAddressException not thrown.");
}

In this case, try/catch makes sense because I know the exception is coming.

But now if I want to write testValidIPAddress(), there's a couple of ways to do it:

Way #1:

@Test
public void testValidIPAddress() throws InvalidIPAddressException
{
    IPAddress addr = new IPAddress("127.0.0.1");
    byte[] octets = addr.getOctets();

    assertTrue(octets[0] == 127);
    assertTrue(octets[1] == 0);
    assertTrue(octets[2] == 0);
    assertTrue(octets[3] == 1);
}

Way #2:

@Test
public void testValidIPAddress()
{
    try
    {
        IPAddress addr = new IPAddress("127.0.0.1");
        byte[] octets = addr.getOctets();

        assertTrue(octets[0] == 127);
        assertTrue(octets[1] == 0);
        assertTrue(octets[2] == 0);
        assertTrue(octets[3] == 1);
    }
    catch (InvalidIPAddressException e)
    {
        fail("InvalidIPAddressException: " + e.getMessage());
    }
}

Is is standard practice to throw unexpected exceptions to JUnit or just deal with them yourself?

Thanks for the help.


Actually, the old style of exception testing is to wrap a try block around the code that throws the exception and then add a fail() statement at the end of the try block. Something like this:

public void testNullParameter() {
    try {
        IPAddress addr = new IPAddress(null);
        fail("InvalidIPAddressException not thrown.");
    } catch(InvalidIPAddressException e) {
        assertNotNull(e.getMessage());
    }
}

This isn't much different from what you wrote but:

  • Your assertTrue(addr.getOctets() == null); is useless.
  • The intend and the syntax are clearer IMO and thus easier to read.
  • Still, this is a bit ugly. But this is where JUnit 4 comes to the rescue as exception testing is one of the biggest improvements in JUnit 4. With JUnit 4, you can now write your test like this:

    @Test (expected=InvalidIPAddressException.class) 
    public void testNullParameter() throws InvalidIPAddressException {
        IPAddress addr = new IPAddress(null);
    }
    

    Nice, isn't it?

    Now, regarding the real question, if I don't expect an exception to be thrown, I'd definitely go for way #1 (because it's less verbose) and let JUnit handle the exception and fail the test as expected.


    For tests where I don't expect an exception, I don't bother to catch it. I let JUnit catch the exception (it does this reliably) and don't cater for it at all beyond declaring the throws cause (if required).

    I note re. your first example that you're not making use of the @expected annotation viz.

    @Test (expected=IndexOutOfBoundsException.class) public void elementAt() {
        int[] intArray = new int[10];
    
        int i = intArray[20]; // Should throw IndexOutOfBoundsException
      }
    

    I use this for all tests that I'm testing for throwing exceptions. It's briefer than the equivalent catch/fail pattern that I had to use with Junit3.


    Since JUnit 4.7 you have the possibility to use an ExpectedException rule and you should use it. The rule gives you the possibility to define exactly the called method where the exception should be thrown in your test code. Moreover, you can easily match a string against the error message of the exception. In your case the code looks like this:

        @Rule
        public ExpectedException expectedException = ExpectedException.none();
    
        @Test
        public void test() {
            //working code here...
            expectedException.expect(InvalidIPAddressException.class);
            IPAddress addr = new IPAddress(null);
        }
    

    UPDATE: In his book Practical Unit Testing with JUnit and Mockito Tomek Kaczanowski argues against the use of ExpectedException, because the rule "breaks the arrange/act/assert [...] flow" of a Unit test (he suggests to use Catch Exception Library instead). Although I can understand his argument, I think using the rule is fine if you do not want to introduce another 3rd-party library (using the rule is better than catching the exception "manually" anyway).

    链接地址: http://www.djcxy.com/p/64094.html

    上一篇: Junit 4测试套件和个人测试课程

    下一篇: JUnit测试抛出异常的错误形式?