JMockit - "Missing invocation to mocked type" 当模拟 System.getProperties()



诚然,我是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());
}

我得到了以下指向"return"方法的异常:

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

此外,我如何同时模拟这两种方法?如果我把它们放在同一个Experts块中(首先使用getProperty()),那么我看不到异常,但System.getProperties()返回真实的系统属性,而不是模拟的属性;尽管getProperty(propertyName)返回模拟值。我觉得这种行为完全不稳定。

我从这篇文章中看到,某些方法不能被嘲笑,但System.getProperties()不在这个列表中:

异常块上的JMockit NullPointerException?

我还发现,2-3年前与JMockit合作的许多SO解决方案现在完全不可编译,所以显然情况发生了很大变化。

System.getProperties()确实是JMockit 1.12中排除在mocking之外的方法之一。当发现新的有问题的JRE方法时,这类排除方法的确切集合在较新版本中可能会发生变化。

不过,没有必要模拟System.getProperty(...)System.getProperties()System类提供了可以替代使用的setPropertysetProperties方法。

希望其他人能发现这一点:

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

请注意,我正在测试一个实用程序,该实用程序将在给定一系列可能的源的情况下,竭尽全力查找属性值。源可以是系统属性、环境变量、meta-inf服务文件、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的框架)的困难,但我理解这些困难确实存在,我可以理解你面临的那种问题。我向你们致敬。

最新更新