5

I am going to create a XML from a string. It looks like

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

public Document createCompleteExportXml(String xmlFilename, String content) {
    try {
        DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();

        //create the XML file here
    } catch (ParserConfigurationException pce) {
        LOGGER.trace("parsing error ", pce);
    }
}

Now I must test if the exception can be caught in a Junit test.

@Test(expected=ParserConfigurationException.class)
public void createCompleteExportXmlWithParseConfigurationException() {
    String xmlFilename = "junitExportTestWithParseConfigurationException.xml";
    String content = "any content";
    XmlFileWriter writer = new XmlFileWriter();
    Document doc = writer.createCompleteExportXml(xmlFilename, content);
}

How can I make this test throw the ParserConfigurationException?

I make my question more concrete: How can I make documentFactory.newDocumentBuilder() not able to work, because "a DocumentBuilder cannot be created which satisfies the configuration requested."? Where is the configuration? How can I change it intentionally to a wrong one?

ostmond
  • 519
  • 1
  • 7
  • 13
  • Which concrete class ir the `documentFacory`? – Jens Sep 03 '14 at 13:35
  • I do not know. I just imported the packages and they work. How can I see which concrete class is being used? – ostmond Sep 04 '14 at 06:42
  • Try to add `System.out.println(documentFactory.getClass().getName())`. Then you should get the concreate classname or use the debugger. This will show you the classname. – Jens Sep 04 '14 at 06:45
  • it is com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl – ostmond Sep 04 '14 at 07:05

3 Answers3

3

Your test is not passing because you are catching precisely ParserConfigurationException in your method, so it's never thrown. To pass the test:

1) change the signature of your method (throwing exception)

public String createCompleteExportXml(String xmlFilename, String content) throws ParserConfigurationException {

2) Throw the ParserConfigurationException. To do this, you can remove the catch block or throw the exception after LOGGER.trace. Example for the second option:

  try {
     //...
  } catch (ParserConfigurationException pce) {
     LOGGER.trace("parsing error ", pce);
     throw pce;
  }

Hope it helps you

[UPDATE]

If you want to simulate a ParserConfigurationException, you can use a framework like Mockito / PowerMock to mock DocumentBuilderFactory and simulate that ParserConfigurationException is thrown when method newDocumentBuilder() is called.

Example:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DocumentBuilderFactory.class)
public class XmlFileWriterTest {

   @Test(expected = ParserConfigurationException.class)
   public void createCompleteExportXmlWithParseConfigurationException() throws Exception {
      String xmlFilename = "junitExportTestWithParseConfigurationException.xml";
      String content = "any content";
      XmlFileWriter writer = new XmlFileWriter();

      // Mock DocumentBuilderFactory: When method newDocumentBuilder() is called, throws a simulated ParserConfigurationException
      DocumentBuilderFactory mockDocumentBuilderFactory = PowerMockito.mock(DocumentBuilderFactory.class);
      PowerMockito.when(mockDocumentBuilderFactory.newDocumentBuilder()).thenThrow(new ParserConfigurationException("Simulated ex"));

      // Mock DocumentBuilderFactory.newInstance(), when is called, returns your mock instance mockDocumentBuilderFactory
      PowerMockito.mockStatic(DocumentBuilderFactory.class);
      PowerMockito.when(DocumentBuilderFactory.newInstance()).thenReturn(mockDocumentBuilderFactory);

      writer.createCompleteExportXml(xmlFilename, content);
   }

This test pass (with previous code suggestions done).

Maven dependencies for powerMock:

<dependency>
     <groupId>org.powermock</groupId>
     <artifactId>powermock-module-junit4</artifactId>
     <version>1.5.4</version>
  </dependency>

  <dependency>
     <groupId>org.powermock</groupId>
     <artifactId>powermock-api-mockito</artifactId>
     <version>1.5.4</version>
  </dependency>

Hope this would be what you are looking for.

You can find more documentation of Mockito and PowerMock

troig
  • 7,072
  • 4
  • 37
  • 63
  • 2
    Thanks. I am going to modify my code according to your suggestion. But acctually I am asking how I can simulate a ParserConfigurationException error. How can I make documentFactory.newDocumentBuilder() not able to work, because "a DocumentBuilder cannot be created which satisfies the configuration requested."? Where is the configuration? How can I change it? – ostmond Sep 04 '14 at 06:39
  • 1
    Hi, take a look to my answer update. You can use a mocking framework like Mockito/PowerMock to simulate the ParserConfigurationException. Hope it helps you. – troig Sep 04 '14 at 07:04
  • The example is so good, I'd almost just needed copy and paste, but when I right clicked the class and run as junit test. I received: message there (see my edition after your code) – ostmond Sep 04 '14 at 13:22
  • Hummm...I ignore how are you running it. With IntellijIdea an maven runs correctly. I think you're forgetting something...Coud you give me more information? – troig Sep 04 '14 at 13:35
  • It seems to be a problem with the versions of junit/PowerMock that are you using. Thake a look to [this post](http://stackoverflow.com/questions/5292440/noclassdeffounderror-when-using-powermock). Try to run with junit 4.9 (if you use power-mock versin that I post in my answer) – troig Sep 04 '14 at 13:39
  • Execuse me. Somehow I checked my junit version is 4.11 and thought it is smaller than 4.4, thus I loaded powermock-module-junit4-legacy. It was a mistake. I corrected that and got it run. I saw it in the log "javax.xml.parsers.ParserConfigurationException: Simulated ex" at XmlFileWriter.createCompleteExportXml(XmlFileWriter.java:35) at XmlFileWriterTestWithPowerMock.testCreateCompleteExportXmlThrowsParserConfigurationException(XmlFileWriterTestWithPowerMock.java:86). But the problem now is, the test is not reported by surefire. I did not see it in target/surefire-report. Do you know why? – ostmond Sep 04 '14 at 14:02
3

As you can see in the source code:

 public DocumentBuilder newDocumentBuilder()
     throws ParserConfigurationException
 {        
     // Check that if a Schema has been specified that neither of the schema properties have been set.

     if (grammar != null && attributes != null) {
         if (attributes.containsKey(JAXPConstants.JAXP_SCHEMA_LANGUAGE)) {
             throw new ParserConfigurationException(
                     SAXMessageFormatter.formatMessage(null,
                     "schema-already-specified", new Object[] {JAXPConstants.JAXP_SCHEMA_LANGUAGE}));
         }
         else if (attributes.containsKey(JAXPConstants.JAXP_SCHEMA_SOURCE)) {
             throw new ParserConfigurationException(
                     SAXMessageFormatter.formatMessage(null,
                     "schema-already-specified", new Object[] {JAXPConstants.JAXP_SCHEMA_SOURCE}));
         }
     }



     try {

         return new DocumentBuilderImpl(this, attributes, features, fSecureProcess);
     } catch (SAXException se) {
         // Handles both SAXNotSupportedException, SAXNotRecognizedException
         throw new ParserConfigurationException(se.getMessage());
     }
 }

if the schema is defined twice the ParserConfigurationException is thrown for instance

eis
  • 51,991
  • 13
  • 150
  • 199
Jens
  • 67,715
  • 15
  • 98
  • 113
1

I don't think either of the existing answers really answered the question, so here's my take on this.

Approach one, taking advantage of com.sun.* parser internals:

(Helper class)

import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import javax.xml.validation.ValidatorHandler;

public class MisconfiguredDocumentBuilderFactory extends DocumentBuilderFactoryImpl {
    @Override
    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {

        super.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
           "http://www.w3.org/2001/XMLSchema");
        super.setSchema(new Schema() {
            @Override
            public Validator newValidator() {
                return null;
            }

            @Override
            public ValidatorHandler newValidatorHandler() {
                return null;
            }
        });
        return super.newDocumentBuilder();
    }
}

(Actual test class)

public class OPClassTest {
    private final static String DOC_BUILDER_PROPERTY_NAME =
        "javax.xml.parsers.DocumentBuilderFactory";

    @Test
    public void testParserConfigurationProblem() {
        System.setProperty(DOC_BUILDER_PROPERTY_NAME,
            MisconfiguredDocumentBuilderFactory.class.getCanonicalName());
        targetClass.createCompleteExportXml("somefilename", "somecontent");
    }
    @After
    public void tearDown() {
        System.clearProperty(DOC_BUILDER_PROPERTY_NAME);
    }
}

Approach 2, without using com.sun.* namespaces

(Helper class)

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

public class MisconfiguredDocumentBuilderFactory2 extends DocumentBuilderFactory {
    @Override
    public DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {

        throw new ParserConfigurationException(
            "this factory is not configured properly");
    }

    @Override
    public void setAttribute(String name, Object value) throws IllegalArgumentException {
        // no-op
    }

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

    @Override
    public void setFeature(String name, boolean value) throws ParserConfigurationException {
        // no-op
    }

    @Override
    public boolean getFeature(String name) throws ParserConfigurationException {
        return false;
    }
}

(Actual test class)

public class OPClassTest {
    private final static String DOC_BUILDER_PROPERTY_NAME =
        "javax.xml.parsers.DocumentBuilderFactory";

    @Test
    public void testParserConfigurationProblem() {
        System.setProperty(DOC_BUILDER_PROPERTY_NAME,
            MisconfiguredDocumentBuilderFactory2.class.getCanonicalName());
        targetClass.createCompleteExportXml("somefilename", "somecontent");
    }
    @After
    public void tearDown() {
        System.clearProperty(DOC_BUILDER_PROPERTY_NAME);
    }
}
eis
  • 51,991
  • 13
  • 150
  • 199