【问题标题】:How to mock static method without powermock如何在没有 powermock 的情况下模拟静态方法
【发布时间】:2017-12-11 12:50:59
【问题描述】:

在 JUnit 中进行测试时,有什么方法可以模拟静态 util 方法吗?

我知道 Powermock 可以模拟静态调用,但我不想使用 Powermock。

还有其他选择吗?

【问题讨论】:

  • 即使使用 Mockito,也无法模拟静态方法。
  • 您是否有不想使用 powermock 的原因?
  • 一定要模拟吗?它是你的代码吗?你可以重写它,这样就不会有静态了? Static methods are death to testability
  • 我注意到你的问题仍然是“开放的”——因为你没有接受答案。请查看并决定是否要accept 回答。或者让我知道我是否可以做些什么来增强我的输入以使其被接受。接受有助于未来的读者确定问题是否已解决,并对花时间回答你的人表示感谢。谢谢!
  • @UweAllner,不使用 powermock 的一个原因是像 JaCoCo 这样的代码覆盖率工具可能不会在代码覆盖率报告中考虑通过 powermock 覆盖的代码。

标签: java unit-testing junit mockito powermock


【解决方案1】:

(我假设你可以使用 Mockito)我没有想到任何专门的东西,但是当涉及到这样的情况时,我倾向于使用以下策略:

1) 在被测类中,将静态直接调用替换为对封装了静态调用本身的包级方法的调用:

public class ToBeTested{

    public void myMethodToTest(){
         ...
         String s = makeStaticWrappedCall();
         ...
    }

    String makeStaticWrappedCall(){
        return Util.staticMethodCall();
    }
}

2) 在测试和模拟封装的包级方法时监视被测类:

public class ToBeTestedTest{

    @Spy
    ToBeTested tbTestedSpy = new ToBeTested();

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void myMethodToTestTest() throws Exception{
       // Arrange
       doReturn("Expected String").when(tbTestedSpy).makeStaticWrappedCall();

       // Act
       tbTestedSpy.myMethodToTest();
    }
}

这是我写的一篇关于间谍的文章,其中包括类似的案例,如果您需要更多见解:sourceartists.com/mockito-spying

【讨论】:

  • 当问题明确询问静态方法时,如何调用构造函数?当所有方法都是静态的时候调用构造函数不是没用吗?在这种情况下,间谍也将毫无用处。
  • 如果类不是最终类,他可以子类化ToBeTested 添加包装方法并测试子类。
  • 当我尝试验证该方法是否被调用时,出现错误Wanted but not invoked:Actually, there were zero interactions with this mock.
【解决方案2】:

当你有静态代码给你的单元测试带来麻烦时;所以你觉得你必须“嘲笑它”,你有以下选择:

  • 您转向 PowerMock(ito)。工作正常。
  • 您转向 JMockit。也很好用。
  • 如果您正在测试自己编写的代码,,您可能想退后一步,问自己:“为什么我要编写现在难以进行单元测试的代码?”

换句话说:如果你想使用一个模拟框架,你必须使用上面列出的那些之一。一方面,这是绝对公平的。 static 是 Java 语言的一部分;那么为什么不使用一个允许你处理它的框架呢?

当然:您的生产代码中仍然有 static 调用。导致紧密耦合,并防止多态性

所以:如果您可以摆脱 static 调用(即使只是使用其他答案中建议的解决方法) - 那就更好了。如果不是:Mockito 无能为力;你需要字节码操作的魔力。 JVM 代理。

【讨论】:

    【解决方案3】:

    您可以使用Mockito(自版本3.4.0)来模拟静态方法。

    给定一个班级Foo

    class Foo{
      static String method() {
        return "foo";
      }
    }
    

    这是测试:

    @Test
    void testMethod() {
        assertEquals("foo", Foo.method());
        try (MockedStatic mocked = Mockito.mockStatic(Foo.class)) {
            mocked.when(Foo::method).thenReturn("bar");
            assertEquals("bar", Foo.method());
            mocked.verify(Foo::method);
        }
        assertEquals("foo", Foo.method());
    }
    

    这需要依赖org.mockito:mockito-inline:3.4.0或更新版本。

    【讨论】:

    • 我使用了 org.mockito:mockito-inline:3.4.0 和更新的版本,并复制了与上面相同的代码。但我仍然收到“MockedStatic”和“mockStatic”的编译错误。它没有显示任何导入语句。我尝试使用 import org.mockito.*;但它不起作用
    • 在使用编辑后的答案后,我收到编译错误,因为“MockedStatic 无法解析为类型”。我们是否需要任何特殊的导入语句。我的 pom.xml 依赖添加为 org.mockitomockito-inline3.5.9
    • @user2000189 你在使用SpringBoot吗?最新版本的spring-boot (2.3.3.RELEASE) 不支持 Mockito 3.4.0。如果要使用,需要强制org.mockito:mockito-core到3.4.0版本。
    • 我使用 spring-boot (2.3.1-RELEASE) 和 mockito-core 作为 3.3.3。
    • @user2000189 强制 mockito-core 到版本 3.4.0 将允许您运行此示例,但请注意其他潜在问题,因为 Spring 尚未正式支持它。
    【解决方案4】:

    我很幸运地做了类似于 Maciej 在上面的回答中建议的事情。在 Java8 中,我喜欢用函数式接口包装这些静态方法,以使它们更直接地注入或模拟。例如:

    public class MyClass {
        private MyStaticWrapper staticWrapper;
    
        public MyClass(final MyStaticWrapper staticWrapper) {
            this.staticWrapper = staticWrapper;
        }
    
        public void main() {
            ...
            staticWrapper.doSomething();
            ...    
        }
    }    
    
    public interface MyStaticWrapper {
        default void doSomething() {
          Util.annoyingUntestableStaticFunction();
        }
    }
    

    【讨论】:

    • 你好,请问你有单元测试的例子吗?
    猜你喜欢
    • 1970-01-01
    • 2020-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-28
    • 2021-05-10
    • 2012-02-09
    • 1970-01-01
    相关资源
    最近更新 更多