为什么Mockito没有模拟静态方法?

Abi*_*idi 252 java methods static mockito

我在这里读了几个关于静态方法的线程,我想我明白了误用/过度使用静态方法会导致的问题.但我并没有真正了解为什么很难模拟静态方法.

我知道其他嘲笑框架,如PowerMock,可以做到这一点,但为什么不能Mockito?

我读过这篇文章,但作者似乎在虔诚地反对这个词static,也许这是我理解不足的原因.

一个简单的解释/链接将是伟大的.

Mat*_*ias 226

我认为原因可能是模拟对象库通常通过在运行时动态创建类来创建模拟(使用cglib).这意味着他们要么在运行时实现一个接口(如果我没有弄错的话就是EasyMock所做的那样),或者他们从类继承到mock(如果我没弄错的话,这就是Mockito所做的).这两种方法都不适用于静态成员,因为您无法使用继承覆盖它们.

模拟静态的唯一方法是在运行时修改类的字节代码,我认为这比继承更复杂.

这是我对它的猜测,因为它的价值......

  • 我认为模拟工具应该能够满足您的需求,而不会让他们知道什么对您更有利.例如,如果我使用的第三方库使用了我需要模拟的静态方法调用,那么能够这样做会很好.模拟框架不能为您提供某些功能的想法因为被视为糟糕的设计而存在根本缺陷. (191认同)
  • @nevvermind呃?高级语言旨在帮助您并获得必要的抽象,以便您可以专注于重要的部分.测试库是一种工具 - 我用来生成质量更好,希望设计更好的代码的工具.测试/模拟库有什么意义,当它有限制时可能意味着当我不得不整合别人设计不良的代码时我无法使用它?似乎没有经过深思熟虑,而**,良好的语言已经**. (13认同)
  • @ Lo-Tan - 就像说一种语言应该能够胜任一切,而不是假设它比你知道的更好.这只是你的虚荣心,因为它们的气势非常强烈.这里的问题是"反/静态"战斗不明确,框架也是如此.我同意我们应该同时拥有两者.但事实**明确的地方,我更喜欢一个*强加*那些事实的框架.这是一种学习方式 - 让您保持正轨的工具.所以你自己不必.但现在每个面条头都可以强加他们所谓的"好设计"."根本上有缺陷"...... (11认同)
  • 值得一提的是,一些TDD/TBD支持者认为缺乏静态方法和构造函数嘲弄是一件好事.他们争辩说,当你发现自己不得不模拟静态方法或构造函数时,这就是穷人类设计的一个指标.例如,在组装代码模块时遵循纯粹的IoC方法,您甚至不需要首先模拟静态或ctors(当然,除非它们是某些黑盒组件的一部分).另见http://giorgiosironi.blogspot.com/2009/11/mocking-static-methods-road-to-hell.html (10认同)
  • 顺便提一下,对于模拟构造函数也是如此.那些也不能通过继承来改变. (7认同)

Jan*_*Jan 28

如果你需要模拟静态方法,它是一个糟糕设计的强大指标.通常,您可以模拟被测试类的依赖关系.如果你的被测试类是指一个静态方法 - 例如java.util.Math#sin--这意味着被测试类正好需要这个实现(例如精度与速度).如果你想从一个具体的正弦实现中抽象出来,你可能需要一个接口(你知道这会发生什么)?

  • 是的,但有时你可能别无选择,例如你需要模拟某个第三方类中的静态方法. (86认同)
  • 没错,但有时我们可能会处理单身人士. (6认同)
  • 好吧,我使用静态方法来提供高级抽象,例如"静态持久性外观".这样的外观使客户端代码远离ORM API的复杂性和低级细节,提供更一致且易于使用的API,同时提供了很大的灵活性. (3认同)
  • 唯一不能通过抽象解决的想法是抽象层次太多……增加抽象层会增加复杂性,而且往往是不必要的。我想到了我见过的一些框架的例子,这些框架试图通过将这个简单的调用包装到一个类中来模拟 System.currentTimeMillis()。我们最终每个方法都有一个单例类,而不是简单的方法——只是为了方便测试。然后当你引入一个直接调用静态方法而不是通过你的单例包装器的 3rd-party dep 时,测试无论如何都会失败...... (3认同)

Ger*_*ica 15

Mockito [3.4.0] 可以模拟静态方法!

  1. 更换mockito-core用的依赖mockito-inline:3.4.0

  2. 静态方法类:

    class Buddy {
      static String name() {
        return "John";
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用新方法Mockito.mockStatic()

    @Test
    void lookMomICanMockStaticMethods() {
      assertThat(Buddy.name()).isEqualTo("John");
    
      try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) {
        theMock.when(Buddy::name).thenReturn("Rafael");
        assertThat(Buddy.name()).isEqualTo("Rafael");
      }
    
      assertThat(Buddy.name()).isEqualTo("John");
    }
    
    Run Code Online (Sandbox Code Playgroud)

    Mockito 仅替换try块内的静态方法。


pet*_*e83 6

如果你需要模拟静态方法,我认真地认为它是代码味道.

  • 访问常用功能的静态方法? - >使用单例实例并注入它
  • 第三方代码? - >将它包装到你自己的界面/委托中(如有必要,也可以将它作为单例)

这对我来说似乎有点过分了,就像Guava这样的库,但你不应该嘲笑这种类型,因为它是逻辑的一部分...(像Iterables.transform(..)
这样的东西)那样你自己的代码保持干净,您可以以干净的方式模拟所有依赖项,并且您有一个针对外部依赖项的反腐败层.我在实践中看到了PowerMock,我们需要的所有类都设计得很糟糕.此外,PowerMock的集成有时会导致严重的问题
(例如https://code.google.com/p/powermock/issues/detail?id=355)

PS:同样适用于私人方法.我认为测试不应该知道私有方法的细节.如果一个类是如此复杂以至于试图模仿私有方法,那么它可能是分裂该类的一个标志......

  • Singleton 会让你遇到各种各样的问题,特别是当你意识到你实际上需要多个实例而现在你需要重构整个系统来实现这一点时。 (3认同)
  • @pete83 我听到你兄弟了。但是,我对测试库或框架有一个问题,要求开发人员更改其设计以满足测试框架的设计/限制。那是 IMO 把车放在马之前,或者尾巴摇狗。 (2认同)
  • 这个论点对我来说毫无意义。单例模式多年来已经失宠,原因太多,无法在此列出。什么是“干净”的代码?如果我有一个类实例方法,它调用返回某些 I/O 操作的静态辅助方法,为什么我不希望在测试中模拟它?那糟糕的设计怎么样?所有这些围绕模拟静态方法的令人绞尽脑汁的事情都没有加起来。模拟方法与测试方法相反。如果实施起来太难,那就直接说出来,然后就完成它 (2认同)

Ale*_*rov 6

作为Gerold Broser 答案的补充,这里有一个使用参数模拟静态方法的示例:

class Buddy {
  static String addHello(String name) {
    return "Hello " + name;
  }
}

...

@Test
void testMockStaticMethods() {
  assertThat(Buddy.addHello("John")).isEqualTo("Hello John");

  try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) {
    theMock.when(() -> Buddy.addHello("John")).thenReturn("Guten Tag John");
    assertThat(Buddy.addHello("John")).isEqualTo("Guten Tag John");
  }

  assertThat(Buddy.addHello("John")).isEqualTo("Hello John");
}
Run Code Online (Sandbox Code Playgroud)