如何轻松模拟 Java (jUnit4) 中的静态方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1203027/
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-10-29 15:36:37  来源:igfitidea点击:

How can I easily mock out a static method in Java (jUnit4)

javaspringstaticmocking

提问by Colin Harrington

How do I easily mock out a static method in Java?

如何轻松模拟 Java 中的静态方法?

I'm using Spring 2.5 and JUnit 4.4

我使用的是 Spring 2.5 和 JUnit 4.4

@Service
public class SomeServiceImpl implements SomeService {

    public Object doSomething() {
        Logger.getLogger(this.class); //a static method invoked.
        // ...
    }
}

I don't control the static method that my service needs to invoke so I cannotrefactor it to be more unit-testable. I've used the Log4J Loggeras an example, but the real static method is similar. It is not an option to change the static method.

我无法控制我的服务需要调用的静态方法,因此我无法将其重构为更具单元可测试性。我以Log4J Logger为例,但真正的静态方法是类似的。 更改静态方法不是一个选项。

Doing Grails work, I'm used to using something like:

做 Grails 工作,我习惯使用类似的东西:

def mockedControl = mockFor(Logger)
mockControl.demand.static.getLogger{Class clazz-> … }
…
mockControl.verify()

How do I do something similar in Java?

我如何在 Java 中做类似的事情?

采纳答案by Colin Harrington

Basically, There isn't an easy way to do this in Java + Spring 2.5 & JUnit 4.4 at the moment.

基本上,目前在 Java + Spring 2.5 和 JUnit 4.4 中没有一种简单的方法可以做到这一点。

Although it is possible to refactor, and abstract away the static call, Refactoring the code isn't the solution that I was looking for.

尽管可以重构并抽象出静态调用,但重构代码并不是我正在寻找的解决方案。

JMockit looked like it would work, but is incompatibile with Spring 2.5 and JUnit 4.4.

JMockit 看起来可以工作,但与 Spring 2.5 和 JUnit 4.4 不兼容。

回答by Jon Skeet

Do you mean you can't control the calling code? Because if you control the calls tothe static method but not the implementation itself, you can easily make that testable. Create a dependency interface with a single method with the same signature as the static method. Your production implementation will just call through to the static method, but anything which currently calls the static method will call via the interface instead.

你的意思是你不能控制调用代码?因为如果你控制调用静态方法而不是实现本身,你可以很容易地作出这样的测试。使用与静态方法具有相同签名的单个方法创建依赖项接口。您的生产实现将只调用静态方法,但当前调用静态方法的任何内容都将通过接口调用。

You can then mock out that interface in the normal way.

然后,您可以以正常方式模拟该界面。

回答by skaffman

The JMockit framework promises to allow mocking of static methods.

JMockit 框架承诺允许模拟静态方法。

https://jmockit.dev.java.net/

https://jmockit.dev.java.net/

In fact, it makes some fairly bold claims, including that static methods are a perfectly valid design choice and their use should not be restricted because of the inadequacy of testing frameworks.

事实上,它提出了一些相当大胆的主张,包括静态方法是一个完全有效的设计选择,不应因为测试框架的不足而限制它们的使用。

Regardless of whether or not such claims are justifiable, the JMockit framework itself is pretty interesting, although I've yet to try it myself.

不管这种说法是否合理,JMockit 框架本身非常有趣,尽管我自己还没有尝试过。

回答by Gerco Dries

PowerMockhas this ability. It can also mock instantiations of objects insidethe class under test. If your tested method calls new Foo(), you can create a mock object for that Foo and replace it in the method you are testing.

PowerMock有这个能力。它还可以模拟对象的实例被测类。如果您的测试方法调用 new Foo(),您可以为该 Foo 创建一个模拟对象并在您正在测试的方法中替换它。

Things like suppressing constructors and static initializers are also possible. All of these things are considered untestable code and thus not recommended to do but if you have legacy code, changing it is not always an option. If you are in that position, PowerMockcan help you.

诸如抑制构造函数和静态初始值设定项之类的事情也是可能的。所有这些事情都被认为是不可测试的代码,因此不建议这样做,但如果您有遗留代码,更改它并不总是一种选择。如果您处于那个位置,PowerMock可以帮助您。

回答by Carl Manaster

public interface LoggerWrapper {
    public Logger getLogger(Class<?> c);
    }
public class RealLoggerWrapper implements LoggerWrapper {
    public Logger getLogger(Class<?> c) {return Logger.getLogger(c);}
    }
public class MockLoggerWrapper implements LoggerWrapper {
    public Logger getLogger(Class<?> c) {return somethingElse;}
    }

回答by Rogério

As another answer above stated, JMockitcan mock static methods (and anything else, as well).

正如上面提到的另一个答案,JMockit可以模拟静态方法(以及其他任何方法)。

It even has direct support for logging frameworks. For example, you could write:

它甚至直接支持日志框架。例如,你可以写:


@UsingMocksAndStubs(Log4jMocks.class)
public class SomeServiceTest
{
    // All test methods in this class will have any calls
    // to the Log4J API automatically stubbed out.
}

Support for JUnit 4.4 was dropped, however. JUnit 3.8, JUnit 4.5+ and TestNG 5.8+ are supported.

但是,放弃了对 JUnit 4.4 的支持。支持 JUnit 3.8、JUnit 4.5+ 和 TestNG 5.8+。

回答by Bill K

That's one of the reason static methods are bad.

这就是静态方法不好的原因之一。

We re-architect ed most of our factories to have setters as well so that we could set mock objects into them. In fact, we came up with something close to dependency injection where a single method acted as a factory for all our singletons.

我们重新构建了大部分工厂,使其也具有 setter,以便我们可以将模拟对象设置到其中。事实上,我们想出了一些接近依赖注入的东西,其中一个方法充当我们所有单例的工厂。

In your case, adding a Logger.setLogger() method (and storing that value) could work. If you have to you could extend the logger class and shadow the getLogger method with your own as well.

在您的情况下,添加 Logger.setLogger() 方法(并存储该值)可以工作。如果必须,您可以扩展记录器类并使用您自己的方法隐藏 getLogger 方法。

回答by Ga?l Marziou

You could use AspectJ to intercept the static method call and do something useful for your test.

您可以使用 AspectJ 拦截静态方法调用并为您的测试做一些有用的事情。