【问题标题】:How do you write a JUnit Test for TransformerException from javax.xml.transform.Transformer.transform如何从 javax.xml.transform.Transformer.transform 为 TransformerException 编写 JUnit 测试
【发布时间】:2018-01-24 21:11:23
【问题描述】:

我一直在尝试编写一个单元测试,试图全面覆盖我的测试类。

我正在尝试测试它是否正确捕获了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(文档对象的实际长度)在复制文档之前没有出现异常。

这里是来自Java 7 API - Exceptions and Error ReportingTransformationException的进一步解释。

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

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

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

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

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

谢谢!

【问题讨论】:

  • 正确的解决方案,IMO,将传递一个无效的Document,它会以某种方式触发TransformerException。并且如果不能创建这样的无效文档,那么catch 块在实践中可能是无法访问的,不需要进行测试。

标签: java unit-testing testing junit mocking


【解决方案1】:

理论上这似乎不是模拟 TransformerException 最漂亮的方法,但经过大量研究和尝试,我想出了模拟 Result 的解决方案> 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

【讨论】:

    猜你喜欢
    • 2021-02-03
    • 2022-11-04
    • 1970-01-01
    • 2022-11-03
    • 2013-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多