JMockit - "缺少对模拟类型的调用"模拟System.getProperties()时

时间:2014-11-04 17:25:27

标签: java unit-testing jmockit

我是JMockit的新手,但我出于某种原因无法模拟System.getProperties()。感谢以下帖子的帮助:

https://stackoverflow.com/questions/25664270/how-can-i-partially-mock-the-system-class-with-jmockit-1-8?lq=1

我可以使用JMockit 1.12成功模拟System.getProperty():

@Test
public void testAddSystemProperty_String() {
    final String propertyName = "foo";
    final String propertyValue = "bar";

    new Expectations(System.class) {{
        System.getProperty(propertyName);
        returns(propertyValue);
    }};

    assertEquals(propertyValue, System.getProperty(propertyName));
}

但是用于模拟getProperties()barfs的怪异类似代码:

@Test
public void testAddSystemProperty_String() {
    final String propertyName = "foo";
    final String propertyValue = "bar";

    final Properties properties = new Properties();
    properties.setProperty(propertyName, propertyValue);

    new Expectations(System.class) {{
        System.getProperties();
        returns(properties);
    }};

    assertEquals(1, System.getProperties().size());
}

我得到以下异常,指向"返回"方法:

Missing invocation to mocked type at this point; 
please make sure such invocations appear only after 
the declaration of a suitable mock field or parameter

另外,我如何同时模拟这两种方法?如果我将它们放在相同的Expectations块中(首先使用getProperty()),那么我没有看到异常,但System.getProperties()返回真实的系统属性,而不是模拟的属性;虽然getProperty(propertyName)返回模拟值。我发现这种行为非常不稳定。

我在这篇文章中看到某些方法无法模拟,但System.getProperties()不在该列表中:

JMockit NullPointerException on Exceptions block?

我也发现,2年前与JMockit合作的很多解决方案现在完全无法编译,所以显然事情发生了很大变化。

2 个答案:

答案 0 :(得分:1)

System.getProperties()确实是在JMockit 1.12中被排除在模拟之外的方法之一。由于找到了新的有问题的JRE方法,这些被排除的方法的确切集合可能会在较新版本中发生变化。

但是,无需模拟System.getProperty(...)System.getProperties()System类提供了setPropertysetProperties方法,可以替代使用。

答案 1 :(得分:0)

希望其他人觉得这很有用:

这可以通过Mockito / PowerMock(1.5.3)解决。

请注意,我正在测试一个实用程序,该实用程序将详尽地尝试在给定可能来源列表的情况下查找属性值。源可以是系统属性,环境变量,元信息服务文件,jndi名称,线程本地,磁盘上的文件,ldap,基本上任何可以执行查找的内容。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassThatDirectlyCallsSystemInCaseItIsNestedLikeInMyCase.class})
public class ConfigPropertyBuilderTest {
    @Test
    public void testAddSystemProperty_String_using_PowerMockito() {
        PowerMockito.mockStatic(System.class);

        PowerMockito.when(System.getProperty(propertyName)).thenReturn(propertyValue);
        PowerMockito.when(System.getProperties()).thenReturn(new Properties() {{
            setProperty(propertyName, propertyValue);
        }});

        // Here is realistic case of calling something that eventually calls System
        // new configBuilder().addEnvironmentVariable(propertyName)
        //                    .addSystemProperty(propertyName)
        //                    .getValue();

        // Here is simplified case:
        assertEquals(1, System.getProperties().size());
        assertEquals(propertyValue, System.getProperty(propertyName));
    }
}

我可以调用System.setProperty(),但是当你开始进入其他来源时,它就变得不那么清楚了。

请注意,我并不特别关心System.getProperty()返回的值;我只想确保在第一次查找失败时调用它。

例如,在上面的代码片段中,环境变量不存在,因此应该调用System.getProperty()。如果环境变量存在(就像在下一个未显示的测试用例中那样),那么我想验证System.getProperty()是否被调用,因为它应该已经短路。

由于使用真实文件,真正的ldap,真实的API等伪造其他来源的困难,并且因为我想验证某些API是被调用还是未被调用,并且因为我想让测试保持一致,我认为嘲笑是正确的方法(即使我可能试图模拟不推荐的东西,以保持一切看起来一致)。如果您不这么认真,请告诉我。

另外,虽然我不了解维护这些模拟框架(特别是基于cglib的框架)的困难,但我理解存在的困难,我可以理解你遇到的那些问题。我的帽子落到了你们身上。