简体   繁体   中英

How do you write a JUnit Test for TransformerException from javax.xml.transform.Transformer.transform

I have been trying to write a unit test in an attempt to reach full coverage on my class under test.

I am trying to test that it properly catches the TransformerException thrown from this method in class DOMUtil:

public final class DOMUtil
{
    // This class is entirely static, so there's no need to ever create an instance.
    private DOMUtil()
    {
        // Do nothing.
    }

   ...
   ...

   /**
     * Returns a String containing XML corresponding to a Document. The String
     * consists of lines, indented to match the Document structure.
     * @param doc - Document to be converted.
     * @return String containing XML or null if an error occurs.
     */
    public static String documentToString(final Document doc)
    {
        try
        {
            // Note that there is no control over many aspects of the conversion,
            // e.g., insignificant whitespace, types of quotes.
            final Transformer tf = TransformerFactory.newInstance().newTransformer();
            tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tf.setOutputProperty(OutputKeys.INDENT,   "yes");
            final Writer out = new StringWriter();
            tf.transform(new DOMSource(doc), new StreamResult(out));
            return out.toString();
        }
        catch (final TransformerException e)
        {
            LOG.error("Error converting Document to String: " + e.getMessage());
            return null;
        }
    }
}

My DOMUtilTest class:

@RunWith(PowerMockRunner.class)
...
...
@PrepareForTest({Document.class, TransformerFactory.class, Transformer.class, DOMUtil.class, DocumentBuilder.class, DocumentBuilderFactory.class})
public class DOMUtilTest
{
    /**
     * Test documentToString with TransformerException 
     */
    @Test(expected=TransformerException.class)
    public void testDocumentToStringTransformerException()
    {
        try
        {
            // Mocking Stuff
            TransformerFactory  fac         = PowerMockito.mock(TransformerFactory.class);
            Transformer         transformer = PowerMockito.mock(Transformer.class);

            // probably only need two of these?? 
            PowerMockito.whenNew(TransformerFactory.class).withNoArguments().thenReturn(fac);
            PowerMockito.whenNew(Transformer.class).withNoArguments().thenReturn(transformer);
            PowerMockito.when(fac.newTransformer(ArgumentMatchers.any(Source.class))).thenReturn(transformer);
            PowerMockito.when(fac.newTransformer()).thenReturn(transformer);

            // spy in results
            PowerMockito.spy(transformer);
            PowerMockito.when(transformer, "transform", ArgumentMatchers.any(Source.class), ArgumentMatchers.any(Result.class)).thenThrow(new TransformerException("Mocked TransformerException"));

            final String result = DOMUtil.documentToString(doc);

            LOG.info("result length: " + result.length());
        }
        catch(Exception e)
        {
            LOG.error("Exception in testDocumentToStringTransformerException: " + e.getMessage());
            fail("Exception in testDocumentToStringTransformerException: " + e.getMessage());
        }
    }
}

I feel like I have tried every possible solution. I have a lot of working tests with similar conditions on other classes/methods. I have tried

  • annotations style mocking/injecting
  • spying
  • ArgumentMatchers.any(DOMSource.class), ArgumentMatchers.any(StreamResult.class) (which gives error: The method any(Class) from the type ArgumentMatchers refers to the missing type DOMSource)

and every other possible way I could think of. Right now the result is still showing: result length: 25706 (the real length of the doc object) without getting an exception before copying the document.

Here is a further explanation of TransformationException from Java 7 API - Exceptions and Error Reporting .

TransformerException is a general exception that occurs during the course of a transformation. A transformer exception may wrap another exception, and if any of the TransformerException.printStackTrace() methods are called on it, it will produce a list of stack dumps, starting from the most recent. The transformer exception also provides a SourceLocator object which indicates where in the source tree or transformation instructions the error occurred. TransformerException.getMessageAndLocation() may be called to get an error message with location info, and TransformerException.getLocationAsString() may be called to get just the location string.

My assumption is the problem is with mocking of the line:

final Transformer tf = TransformerFactory.newInstance().newTransformer();

Has anybody encountered a situation like this, with JUnit and PowerMockito?

If anybody could point me in the right direction or show me how to solve this any help would be greatly appreciated.

Thanks!

In theory this does not seem like the prettiest way of mocking the TransformerException , but upon a lot of research and attempts, I came up with the solution of mocking the Result outputTarget argument of tf.transform :

@Test
public void testDocumentToStringTransformerException() throws Exception
{
    // Mocking Stuff
    PowerMockito.whenNew(StringWriter.class).withNoArguments().thenReturn(null);

    // Execute Method Under Test
    final String result = DOMUtil.documentToString(doc);

    // Verify Result
    assertNull(result);
}

Which will ultimately give:

 XML-22122: (Fatal Error) Invalid StreamResult - OutputStream, Writer, and SystemId are null.

encapsulated into a TransformerException. This allows me to test the TransformerException branches of code coverage.

So the good news is it is possible with mocking, instead of trying to figure out how to manipulate a Document object to throw the exception. Also don't have to mock as many objects as I initially thought.

(References to TransformerException here: Java 7 API - Exceptions and Error Reporting )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM