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

发布于 2024-08-02 00:08:42 字数 159 浏览 4 评论 0原文

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

JMock 和 EasyMock 都因最终方法而窒息,而且我没有看到测试静态调用的好方法。 有什么技术可以测试这些?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

你对谁都笑 2024-08-09 00:08:42

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

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

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

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

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

这是example 测试模拟静态最终方法,该示例还展示了如何模拟其他类型:

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

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.

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

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.

Here's an example test 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());
    }
}
作死小能手 2024-08-09 00:08:42

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

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

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

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

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.

所谓喜欢 2024-08-09 00:08:42

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

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

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

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.

你的他你的她 2024-08-09 00:08:42

正如已经指出的,可以使用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
    }};
}

As already pointed out, JMockit can be used.
An example:

@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
    }};
}
少跟Wǒ拽 2024-08-09 00:08:42

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

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.

勿忘心安 2024-08-09 00:08:42

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

JMock together with JDave can mock final methods and classes, if you need to. Here
are 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文