我在这里读了一些关于静态方法的文章,我想我理解滥用/过度使用静态方法会导致的问题。但是我并没有真正理解为什么很难模拟静态方法。

我知道其他的模拟框架,比如PowerMock,可以做到这一点,但是为什么Mockito不能呢?

我读了这篇文章,但作者似乎虔诚地反对静态这个词,也许是我的理解能力差。

一个简单的解释/链接就好了。


在某些情况下,静态方法可能很难测试,特别是当它们需要被模拟时,这就是大多数模拟框架不支持它们的原因。我发现这篇博客文章在决定如何模拟静态方法和类方面非常有用。

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

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

这是我的猜测,无论如何……

如果您需要模拟一个静态方法,这是一个糟糕设计的强烈指示器。通常,您会模拟被测试类的依赖关系。如果你的待测类引用了一个静态方法,比如java.util。例如,Math#sin——这意味着被测试的类恰好需要这种实现(例如,准确性vs.速度)。如果你想从一个具体的窦实现中抽象出来,你可能需要一个接口(你知道这是要去哪里)?

我真的认为,如果您也需要模拟静态方法,这就是代码的味道。

访问公共功能的静态方法?->使用一个单例实例并注入 第三方代码?把它包装到你自己的接口/委托中(如果有必要,也可以让它成为一个单例)

The only time this seems overkill to me, is libs like Guava, but you shouldn't need to mock this kind anyway cause it's part of the logic... (stuff like Iterables.transform(..)) That way your own code stays clean, you can mock out all your dependencies in a clean way, and you have an anti corruption layer against external dependencies. I've seen PowerMock in practice and all the classes we needed it for were poorly designed. Also the integration of PowerMock at times caused serious problems(e.g. https://code.google.com/p/powermock/issues/detail?id=355)

PS:私有方法也是如此。我认为测试不应该知道私有方法的细节。如果一个类非常复杂,以至于它倾向于模拟私有方法,这可能是拆分该类的标志……

Mockito返回对象,但静态意味着“类级别,而不是对象级别”,因此Mockito将为静态提供空指针异常。

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

Replace mockito-core dependency with mockito-inline:3.4.0. Class with static method: class Buddy { static String name() { return "John"; } } Use new method 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"); } Mockito replaces the static method within the try block only.

作为对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");
}