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

时间:2009-07-29 21:02:53

标签: java spring static mocking

如何在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 Logger作为示例,但真正的静态方法是类似的。 不能更改静态方法。

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

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

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

8 个答案:

答案 0 :(得分:14)

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

然后你可以用正常方式模拟出那个界面。

答案 1 :(得分:4)

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

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

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

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

答案 2 :(得分:4)

PowerMock具备此功能。它还可以模拟测试中的对象内部的实例化。如果您测试的方法调用了新的Foo(),您可以为该Foo创建一个模拟对象,并在您正在测试的方法中替换它。

抑制构造函数和静态初始化程序之类的东西也是可能的。所有这些都被认为是不可测试的代码,因此不建议这样做,但如果您有遗留代码,更改它并不总是一个选项。如果您处于该职位,PowerMock可以帮助您。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

正如上面提到的另一个答案,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+。

答案 5 :(得分:0)

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

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

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

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