Java 如何测试公用工程项目的最终方法和静态方法?

Java 如何测试公用工程项目的最终方法和静态方法?,java,junit,Java,Junit,我正在尝试为一个项目实现单元测试,它使用一个遗留的“实用工具”项目,该项目充斥着静态方法,并且许多类是final或它们的方法是final。我根本无法更新遗留项目 JMock和EasyMock都使用了final方法,我看不到测试静态调用的好方法。有哪些技术可以测试这些方法?如果您能够重构代码,您可以将对final/static方法的调用封装在简单的实例方法中,例如: protected Foo doBar(String name) { return Utility.doBar(name);

我正在尝试为一个项目实现单元测试,它使用一个遗留的“实用工具”项目,该项目充斥着静态方法,并且许多类是final或它们的方法是final。我根本无法更新遗留项目


JMock和EasyMock都使用了final方法,我看不到测试静态调用的好方法。有哪些技术可以测试这些方法?

如果您能够重构代码,您可以将对final/static方法的调用封装在简单的实例方法中,例如:

protected Foo doBar(String name) {
    return Utility.doBar(name);
}
这允许您重写单元测试中的包装器方法,以返回Foo的模拟实例

或者,您可以使用,它扩展Easymock(和Mockito)以允许模拟最终和静态方法:

PowerMock是一个框架,它使用更强大的功能扩展了其他模拟库,如EasyMock。PowerMock使用自定义类加载器和字节码操作来支持模拟静态方法、构造函数、最终类和方法、私有方法、删除静态初始值设定项等等

下面是一个模拟静态final方法的测试,该示例还显示了如何模拟其他一些类型:

@Test
public void testMockStaticFinal() throws Exception {
    mockStatic(StaticService.class);
    String expected = "Hello altered World";
    expect(StaticService.sayFinal("hello")).andReturn("Hello altered World");
    replay(StaticService.class);

    String actual = StaticService.sayFinal("hello");

    verify(StaticService.class);
    assertEquals("Expected and actual did not match", expected, actual);

    // Singleton still be mocked by now.
    try {
        StaticService.sayFinal("world");
            fail("Should throw AssertionError!");
    } catch (AssertionError e) {
        assertEquals("\n  Unexpected method call sayFinal(\"world\"):", 
            e.getMessage());
    }
}

间接/依赖注入的级别如何

由于遗留实用程序项目是您的依赖项,因此创建一个接口将其与代码分离。现在,此接口的实际/生产实现将委托给遗留实用程序方法

public LegacyActions : ILegacyActions
{
  public void SomeMethod() { // delegates to final/static legacy utility method }
}

对于您的测试,您可以创建该接口的模拟并避免与遗留实用程序TIGIE交互。

< P>如果您的不可重构方法使用像JNDI之类的东西连接到另一个服务,我会考虑启动JDNI服务并用您所控制的存根填充它。这是一种痛苦,但相对简单。这可能意味着设置一个数据库或JMS侦听器或其他什么,但是应该有一个轻量级java实现,您可以将其放入测试中

允许您模拟静态方法和最终类。我假设它在fu中使用了一些classloadin,尽管我还没有真正研究过它

JMockit API允许 对任何类型的 方法调用(在接口上, 抽象类、具体类或最终类 非最终类和静态类 方法),以及在课堂上 通过任何 构造器

正如已经指出的,可以使用。 例如:

@Test
public void mockStaticAndFinalMethods(@Mocked LegacyService mock) {
    new Expectations() {{
        LegacyService.staticMethod("hello"); result = "Hello altered World";
    }};

    String actual = LegacyService.staticMethod("hello");
    new LegacyService().finalMethod(123, "test");

    assertEquals("Hello altered World", actual);

    new Verifications() {{
        mock.finalMethod(123, "test"); // verify this call occurred at least once
    }};
}
@测试
public void mockstatic和finalmethods(@Mocked LegacyService mock){
新期望(){{
LegacyService.staticMethod(“hello”);result=“hello altered World”;
}};
String actual=LegacyService.staticMethod(“hello”);
新的LegacyService().最终方法(123,“测试”);
assertEquals(“你好,改变的世界”,实际);
新的核查(){{
finalMethod(123,“test”);//验证此调用是否至少发生过一次
}};
}

如果需要,JMock和JDave可以模拟最终的方法和类。
这些是说明。也就是说,我将把这个遗留代码(正如其他人已经建议的那样)视为一个外部依赖项,并构建接口并模拟这些接口。这是另一个间接层,但由于您无法更改遗留代码,因此它似乎是一个合理的层。

是否有任何理由需要使用模拟框架?是的,方法调用使用JNDI属性连接到数据库和JMS,我不想为我的测试实现所有的部分。你能重构任何遗留代码来帮助你吗?不,更新了问题以反映这对集成测试来说没问题,但单元测试不需要数据库你可以模拟数据库,然后把这个模拟的东西注入JNDI里。@Rich Seller:我同意,但是如果你有一些鸡蛋,你可以吃培根和鸡蛋,如果你有一些培根的话。我的意思是,最初的问题表明,他无法重构代码,希望添加测试。有时你必须使用你拥有的,而不是你想要的。这看起来像是我需要的,因为你知道它是否在Maven构建中工作吗?我还没有尝试过,但它看起来是这样的:+1,因为它引入了一个我不知道的测试工具。:)最后,我必须用非静态函数包装静态函数才能通过此测试,不可能:((