【问题标题】:How to mock a variable that uses another static final variable in another class如何模拟在另一个类中使用另一个静态最终变量的变量
【发布时间】:2018-02-13 15:59:10
【问题描述】:

我在 junit 中设置此测试方法时遇到问题。我正在尝试使用新时间戳测试 updateConfigDates 的方法。在这里请多多包涵,因为提供的信息很多,看起来不太漂亮。

这是正在测试的方法。

public static Document updateConfigDates(Document doc, Timestamp configDate)
{
    //Format timestamp to string
    String configTimestampStr = GTR_DATE_FORMAT.format(configDate) + "Z";

    //Change configuration date for all nodes in GTR
    NodeList configIDNodes = doc.getElementsByTagName("ConfigDate");
    for (Element cidNode : new DOMUtil.ElementList(configIDNodes))
    {
        cidNode.setTextContent(configTimestampStr);
    }

    return doc;
}

问题出在:

    //Format timestamp to string
    String configTimestampStr = GTR_DATE_FORMAT.format(configDate) + "Z";

使用公共的 final 类常量

public final class Constants
{
    /** GTR Timestamp formatter without micro-second */
    public static final FastDateFormat GTR_DATE_FORMAT 
                           = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
}

这是我当前的测试用例

@Test
public void testUpdateConfigDates() throws Exception
{
    // check if configDate in document is expected
    String docConfigDate = doc.getElementsByTagName("ConfigDate").item(0).getTextContent();
    assertEquals(docConfigDate, "2012-02-22T16:07:27Z");

    LOG.info("docConfigDate: " + docConfigDate);

    // variables
    Timestamp newConfigDate = Timestamp.valueOf("2009-07-29 13:24:11");

    // Mocking statics
    //PowerMockito.mockStatic(GTRConstants.class);

    // String configTimestampStr = GTR_DATE_FORMAT.format(configDate) + "Z";
    //common.setFinalStatic(GTRUtility.class.getDeclaredField("configTimestampStr"), "yyyy-MM-dd'T'HH:mm:ss" + "Z");
    common.setFinalStatic(Constants.class.getDeclaredField("GTR_DATE_FORMAT"), FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss"));

    /* setFinalStatic ExceptionInInitializerError information below
     * 
     * Field#set(Object, Object) can be used to set static fields.
     * If you try to set the field of an uninitialized class, the JVM will first try to initialize the class.
     * If a failure occurs, then set will throw a ExceptionInInitializerError.
     */

    // execute method under test
    doc = gtrUtility.updateConfigDates(doc, newConfigDate);

    // verify expectations
    docConfigDate = doc.getElementsByTagName("ConfigDate").item(0).getTextContent();
    assertEquals(docConfigDate, "2009-07-29 13:24:11");
}

使用setFinalStatic solution posted here,它成功地与我正在进行的一些其他测试一起工作。

public static void setFinalStatic(Field field, Object newValue) throws Exception
{
    field.setAccessible(true);
    // remove final modifier from field
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, newValue);
}

我尝试了几种解决方案:

  • 我最近的新方法,使用setFinalStatic method
  • 使用 PowerMockito 和 Mockito 生成模拟常量。
  • 使用 PowerMockito 和 Mockito 模拟 GTR_DATE_FORMAT 实例。
  • 使用 PowerMockito 和 Mockito 模拟 FastDateFormat。

所有这些都失败了。我认为 setFinalStatic 方法是最接近的,但是我目前收到错误 ExceptionInInitializerError (正如用户 here 和 Oracle Doc here 中所讨论的)

【问题讨论】:

  • 为什么要模拟 GTR_DATE_FORMAT?为什么不只提供一个可格式化的值并让格式化 eb part 成为您的测试。感觉试图从测试流程中排除实际的格式化程序是一个太过分的步骤。或者,如果您确实需要从测试流程中排除格式化程序,那么您可以将其隐藏在接口后面,并在您的测试用例调用该方法时将该接口的模拟实例提供给updateConfigDates()
  • 你可能会使用 Power mock 来实现你想要做的事情
  • 最初,我一直在该行收到错误,即使提供了可格式化的值。所以我想模拟一下,我认为这将使范围更多地保持在我的测试类范围内。
  • 或者,您可以修改方法以接受FastDateFormat 参数。
  • 我一直在使用 PowerMockito。我试过模拟结果和对象实例,但都没有成功。

标签: java unit-testing junit mocking powermockito


【解决方案1】:

事实证明,我的问题的答案是常量文件正在执行一些我必须模拟的初始化方法(在本例中:对象上的 safeGetTypeId(x),来自数据库)。

当我意识到 updateObjectId 硬编码测试正在工作并且 updateTimestamp 使用常量类 GTR_DATE_FORMAT 时,我应该更快地注意到这一点。

构造函数是空的,我没想到会执行任何其他操作。

这是一个极端而复杂的问题,所以我可能会删除这个问题,但是如果它将来可以帮助某人,我会留下它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 2012-12-21
    • 1970-01-01
    • 2012-09-06
    • 2015-10-04
    • 1970-01-01
    相关资源
    最近更新 更多