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

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

我正在使用Spring 2.5和JUnit 4.4

@Service public class SomeServiceImpl implements SomeService { public Object doSomething() { Logger.getLogger(this.class); //a static method invoked. // ... } } 

我不控制我的服务需要调用的静态方法,所以我不能将它重构为更可unit testing。 我使用Log4J Logger作为示例,但真正的静态方法类似。 不能更改静态方法。

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

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

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

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

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

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

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

实际上,它提出了一些相当大胆的主张,包括静态方法是一种完全有效的设计选择,并且由于测试框架的不足,它们的使用不应受到限制。

无论这些声明是否合理,JMockit框架本身都非常有趣,尽管我还没有尝试过。

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

像压缩构造函数和静态初始化器这样的东西也是可能的。 所有这些都被认为是不可测试的代码,因此不建议这样做,但如果你有遗留代码,那么改变它并不总是一个选择。 如果你在那个位置, PowerMock可以帮助你。

 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;} } 

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

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

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

但是,对JUnit 4.4的支持被删除了。 支持JUnit 3.8,JUnit 4.5+和TestNG 5.8+。

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

我们为大多数工厂重新设计了setter,以便我们可以将mock对象设置到它们中。 实际上,我们提出了一些接近dependency injection的东西,其中一个方法充当了我们所有单身人士的工厂。

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

您可以使用AspectJ拦截静态方法调用并为测试执行一些有用的操作。

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

虽然可以重构和抽象静态调用,但重构代码并不是我想要的解决方案。

JMockit看起来会起作用,但与Spring 2.5和JUnit 4.4不兼容。