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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1293337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 07:53:06  来源:igfitidea点击:

How can I test final and static methods of a utility project?

javajunit

提问by talk to frank

I'm trying to implement unit testing for aproject, it uses a legacy "utility" project that is littered with static methods and many of the classes are final or their methods are final. I'm not able to update the legacy project at all.

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

JMock and EasyMock both choke on final methods, and I don't see a nice way to test the static calls. What techniques are there to test these?

JMock 和 EasyMock 都阻塞在 final 方法上,我没有看到测试静态调用的好方法。有哪些技术可以测试这些?

采纳答案by Rich Seller

If you're able to refactor your code, you can wrap your calls to the final/static methods in simple instance methods, for example:

如果您能够重构您的代码,您可以将您对最终/静态方法的调用包装在简单的实例方法中,例如:

protected Foo doBar(String name) {
    return Utility.doBar(name);
}

This allows you to override your wrapper method in the unit test to return a mock instance of Foo.

这允许您在单元测试中覆盖您的包装器方法以返回 Foo 的模拟实例。

Alternatively you can use Powermock, which extends Easymock (and Mockito) to allow mocking of final and static methods:

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

PowerMock is a framework that extend other mock libraries such as EasyMock with more powerful capabilities. PowerMock uses a custom classloader and bytecode manipulation to enable mocking of static methods, constructors, final classes and methods, private methods, removal of static initializers and more.

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

Here's an exampletest mocking a static final method, the example shows how to mock some other types too:

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

@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());
    }
}

回答by Gishu

How about a level of indirection / Dependency Injection?

间接/依赖注入级别怎么样?

Since the legacy utility project is your dependency, create an interface to separate it out from your code. Now your real/production implementation of this interface delegates to the legacy utility methods.

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

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

For your tests, you can create a mock of this interface and avoid interacting with the legacy utility thingie.

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

回答by u7867

If your non-refactorable method uses something like JNDI to connect to another service, I'd consider starting a JDNI service and populating it with stubs which you control. It's a pain but relatively straightforward. It may mean setting up a database or JMS listener or whatever but there should be a lightweight java implementation you can drop into the tests.

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

回答by skaffman

JMockitallows you to mock static methods and final classes. I assume it uses some classloadin-fu, although I haven't really looked into it.

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

JMockit Expectations API allows expectations to be set on any kind of method invocation (on interfaces, abstract classes, concrete final or non final classes, and on static methods), as well as on class instantiation through any constructors.

JMockit Expectations API 允许对任何类型的方法调用(接口、抽象类、具体的最终或非最终类以及静态方法)以及通过任何构造函数的类实例化设置期望。

回答by Rogério

As already pointed out, JMockitcan be used. An example:

正如已经指出的,可以使用JMockit。一个例子:

@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
    }};
}

回答by Yishai

JMock together with JDave can mock final methods and classes, if you need to. Hereare instructions. That being said I would treat this legacy code (as others have suggested already) as an external dependency and build interfaces and mock those. It is another layer of indirection, but since you can't change that legacy code, it seems to be a reasonable one.

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