如何轻松地模拟出Java中的静态方法?
我使用Spring 2.5和JUnit 4.4
@Service
public class SomeServiceImpl implements SomeService {
public Object doSomething() {
Logger.getLogger(this.class); //a static method invoked.
// ...
}
}
我无法控制,我的服务需要调用静态方法,所以我不能重构它更单元测试。 我已经使用了Log4J的记录仪作为一个例子,但真正的静态方法类似。 这是不改变的静态方法的选择。
这样做的Grails的工作,我已经习惯了使用类似:
def mockedControl = mockFor(Logger)
mockControl.demand.static.getLogger{Class clazz-> … }
…
mockControl.verify()
我该怎么做在Java中类似的事情?
你的意思是你无法控制的调用代码? 因为如果你控制的静态方法的调用而不是实现本身,你可以很容易地作出这样的测试。 创建具有相同签名的静态方法单一方法的依赖接口。 你的生产实施将只需要调用通过对静态方法,但任何目前调用静态方法,而不是通过接口调用。
然后,您可以模拟出正常方式接口。
该JMockit框架承诺允许的静态方法嘲弄。
https://jmockit.dev.java.net/
事实上,它使一些相当大胆的索赔,包括静态方法是非常有效的设计选择和它们的使用不应该因为测试框架的不足之处加以限制。
不管这样的索赔是否是合理的,在JMockit框架本身是非常有趣的,虽然我还没有自己试试吧。
PowerMock有这个能力。 它还可以模拟被测类的内部对象的实例。 如果你的测试方法调用新的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+支持。
这就是原因静态方法是坏的。
我们重新构建编大多数我们的工厂有制定者,以及使我们可以设置模拟对象放进去。 事实上,我们想出了一些接近依赖注入其中一个方法充当一个工厂为我们所有的单身人士。
在你的情况下,增加一个Logger.setLogger()方法(和存储该值)可以工作。 如果你有,你可以扩展记录器类,并用你自己的影子的getLogger方法为好。
你可以使用AspectJ来截取静态方法调用,并做一些对你有用的测试。
基本上,没有一个简单的方法在目前要做到这一点在Java中+春2.5 JUnit 4.4的。
虽然可以重构,并抽象掉了静态调用,重构代码是不是我一直在寻找解决方案。
JMockit看起来像它的工作,但incompatibile与Spring 2.5和JUnit 4.4。