«« Access to Information Java Unit Testing Exceptions with JUnit »»
blog header image
Code Covering ParserConfigurationException

Java 1.4 has a new library that deals with XML built into it called JAXP. In order to parse an XML document, you need a Document object. You can get a Document object from a DocumentBuilder object and you get DocumentBuilder objects from a DocumentBuilderFactory object. Yeah, I'm not sure if I like that either but it's the way they do it.

This is where my 1 single line of uncovered code is: when you use the newInstance() method of the DocumentBuilderFactory to create a new DocumentBuilder if a "builder cannot be created with the configuration requested" the newDocumentBuilder() method throws ParserConfigurationException. This exception is a checked exception and should be dealt with and not terminate the program.

Here was my method. How could I throw that exception and cover the catch line?

protected void setupBuilder()
{
   try
   {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      this.builder = factory.newDocumentBuilder();
   }
   catch (ParserConfigurationException e)
   {
      e.printStackTrace();
   }
}

I'm going to turn it into an unchecked runtime exception by catching it and throwing IllegalArgumentException. In the context I'm using the factory it would be a programming error to misconfigure it and then try to use it to create a builder. Programming errors, like using a method improperly, usually throw unchecked runtime exceptions like IllegalArgumentException.

You might be wondering why I don't just make this function throw ParserConfigurationException. Because then I'd have to explicitly use a try/catch whenever I used it. If I change the exception to a runtime exception, that is not the case. So here's what I came up with:

protected void setupBuilder(DocumentBuilderFactory factory)
{
   try
   {
      this.builder = factory.newDocumentBuilder();
   }
   catch (ParserConfigurationException e)
   {
      throw new IllegalArgumentException("parameter factor is invalid: " + e);
   }
}

The key is to give the method a parameter that will change it's behaviour or output. Without an input parameter you cannot control the output and have good unit testing. Here are the test methods I wrote to completely cover the method:

public void testSetupBuilder_Null()
{
   XMLRepository repository = XMLRepository.getInstance();

   try
   {
      repository.setupBuilder(null);
   }
   catch (NullPointerException npe)
   {
      return;
   }

   fail("Expected NullPointerException");
}

public void testSetupBuilder_DefaultFactory()
{
   XMLRepository repository = XMLRepository.getInstance();

   DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
   repository.setupBuilder(factory);
}

public void testSetupBuilder_InvalidFactory()
{
   XMLRepository repository = XMLRepository.getInstance();

   DocumentBuilderFactory factory = new DocumentBuilderFactory()
   {
      public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException
      {
         throw new ParserConfigurationException("test");
      }

      public void setAttribute(String name, Object value) throws IllegalArgumentException
      {

      }

      public Object getAttribute(String name) throws IllegalArgumentException
      {
         return null;
      }
   };

   try
   {
      repository.setupBuilder(factory);
   }
   catch (IllegalArgumentException iae)
   {
      return;
   }

   fail("Expected IllegalArgumentException");
}

The last test just implements a custom factory that throws ParserConfigurationException when newDocumentBuilder() is called. It's a pretty simple way to cover the catch block. So now I have 100% code coverage for AudioMan. Sweeeet.

Posted at February 13, 2004 at 03:52 AM EST
Last updated February 13, 2004 at 03:52 AM EST
Comments

You should have:

public void testSetupBuilder_Null()
{
XMLRepository repository = XMLRepository.getInstance();

try
{
repository.setupBuilder(null);
fail("Expected NullPointerException");
}
catch (NullPointerException npe)
{
return;
}
}


Instead of:

public void testSetupBuilder_Null()
{
XMLRepository repository = XMLRepository.getInstance();

try
{
repository.setupBuilder(null);
}
catch (NullPointerException npe)
{
return;
}

fail("Expected NullPointerException");
}

This is because you want to catch inside the try block so if it's not thrown then you'll know.
If you call fail(String) then the test always fails.

» Posted by: Aleks at February 13, 2004 10:50 AM

Same for the
public void testSetupBuilder_InvalidFactory()

» Posted by: Aleks at February 13, 2004 10:52 AM

It doesn't fail if the exception is thrown and then caught. The return; call stops the function before it can get to fail(""). This is a pretty standard way to test exceptions and has been documented on http://www.junit.org.

» Posted by: Ryan at February 13, 2004 01:21 PM

I didn't see that return. Good job. Will try not to post stupid comments again.

» Posted by: Aleks at February 13, 2004 01:39 PM

It's OK, you inspired my next post. :)

» Posted by: Ryan at February 13, 2004 02:10 PM
Google
 
Search scope: Web ryanlowe.ca