如何从javax.xml.transform.Transformer.transform为TransformerException编写JUnit测试

时间:2018-01-24 21:11:24

标签: java unit-testing testing junit mocking

我一直在尝试编写一个单元测试,试图在我的课堂上完全覆盖。

我正在尝试测试它是否正确捕获了DOMUtil类中此方法抛出的 TransformerException

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;
        }
    }
}

我的DOMUtilTest类:

@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());
        }
    }
}

我觉得我已经尝试了所有可能的解决方案。我在其他类/方法上有很多类似条件的工作测试。我试过了

  • 注释样式模拟/注入
  • 间谍
  • ArgumentMatchers.any(DOMSource.class),ArgumentMatchers.any(StreamResult.class)(它给出错误:ArgumentMatchers类型中的方法any(Class)引用缺少的类型DOMSource)

以及我能想到的其他所有方式。现在结果仍然显示:结果长度:25706(doc对象的实际长度),在复制文档之前没有出现异常。

以下是Java 7 API - Exceptions and Error Reporting TransformationException 的进一步说明。

  

TransformerException是一个常见的异常,发生在   转型的过程。变压器异常可以包装另一个   异常,如果有任何TransformerException.printStackTrace()   在它上面调用方法,它会产生一个堆栈转储列表,   从最近开始。变压器例外也提供   一个SourceLocator对象,它指示源树中的位置或   转换指令发生错误。   可以调用TransformerException.getMessageAndLocation()来获取   带位置信息的错误消息,和   可以调用TransformerException.getLocationAsString()来获取   位置字符串。

我的假设是问题在于嘲笑这条线:

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

有没有人遇到过这样的情况,有JUnit和PowerMockito?

如果有人能指出我正确的方向或告诉我如何解决这个问题,我们将不胜感激。

谢谢!

1 个答案:

答案 0 :(得分:0)

从理论上讲,这似乎不是模仿 TransformerException 最漂亮的方式,但经过大量的研究和尝试,我想出了嘲笑结果 tf.transform 的outputTarget参数:

@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);
}

最终会给出:

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

封装成TransformerException。这允许我测试代码覆盖的TransformerException分支。

所以好消息是它可以通过模拟,而不是试图弄清楚如何操纵Document对象来抛出异常。也不必像我最初想的那样模拟尽可能多的对象。

(这里引用TransformerException:Java 7 API - Exceptions and Error Reporting