用Mockito模拟静态方法

问题

我写了一个工厂来生产java.sql.Connection个对象:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

我想验证传递给DriverManager.getConnection的参数,但我不知道如何模拟静态方法。我正在使用JUnit 4和Mockito来测试我的测试用例。有没有一种很好的方法来模拟/验证这个特定的用例?


#1 热门回答(239 赞)

使用PowerMockito在Mockito的顶部。

示例代码:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class Mocker {

    @Test
    public void testName() throws Exception {

        //given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);

        //when
        sut.execute();

        //then
        PowerMockito.verifyStatic();
        DriverManager.getConnection(...);

    }

更多信息:

  • 为什么Mockito不会模拟静态方法?

#2 热门回答(41 赞)

避免使用无法避免使用的静态方法的典型策略是创建包装对象并使用包装器对象。

包装器对象成为真实静态类的外观,你不会测试它们。

包装器对象可能是这样的

public class Slf4jMdcWrapper {
    public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper();

    public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() {
        return MDC.getWhateverIWant();
    }
}

最后,你的测试类可以使用此单例对象,例如,具有用于实际使用的默认构造函数:

public class SomeClassUnderTest {
    final Slf4jMdcWrapper myMockableObject;

    /**constructor used by CDI or whatever real life use case */
    public myClassUnderTestContructor() {
        this.myMockableObject = Slf4jMdcWrapper.SINGLETON;
    }

    /**constructor used in tests*/
    myClassUnderTestContructor(Slf4jMdcWrapper myMock) {
        this.myMockableObject = myMock;
    }
}

在这里,你有一个可以轻松测试的类,因为你不直接使用静态方法的类。

如果你正在使用CDI并且可以使用@Inject注释,那么它就更容易了。只需让你的Wrapper bean @ApplicationScoped,将这个东西注入一个协作者(你甚至不需要凌乱的构造函数进行测试),然后继续进行模拟。


#3 热门回答(15 赞)

如前所述,你无法使用mockito模拟静态方法。

如果无法更改测试框架,则可以执行以下操作:

为DriverManager创建一个接口,模拟这个接口,通过某种依赖注入注入它并验证该模拟。