用Mockito嘲笑静态方法

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

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

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

在Mockito上使用PowerMockito 。

示例代码:

 @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不能模拟静态方法?

避免使用静态方法的典型策略是通过创build包装对象并使用包装器对象来代替。

包装对象成为真正的静态类的外观,而不是testing它们。

一个包装对象可能是类似的东西

 public class Slf4jMdcWrapper { public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper(); public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() { return MDC.getWhateverIWant(); } } 

最后,被testing的类可以使用这个单例对象,例如,具有实际使用的默认构造函数:

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

在这里,你有一个可以很容易地testing的类,因为你不直接使用静态方法的类。

如果您正在使用CDI并可以使用@Inject注释,那么它更加容易。 只要让你的Wrapper bean @ApplicationScoped,把这个东西注入为一个合作者(你甚至不需要混乱的构造函数来testing),然后继续嘲笑。

如前所述,你不能用mockito来嘲笑静态方法。

如果更改您的testing框架不是一个选项,您可以执行以下操作:

为DriverManager创build一个接口,模拟这个接口,通过某种dependency injection来注入它并validation这个模拟。

要嘲笑静态方法,你应该使用Powermock的看看: https : //github.com/powermock/powermock/wiki/MockStatic 。 Mockito 不提供此function。

你可以阅读很好的关于mockito的文章: http: //refcardz.dzone.com/refcardz/mockito

我有一个类似的问题。 接受的答案不适用于我,直到我做了更改: @PrepareForTest(TheClassYouWriteTestFor.class)

而且我不必使用BDDMockito

我的课:

 public class SmokeRouteBuilder { public static String smokeMessageId() { try { return InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { log.error("Exception occurred while fetching localhost address", e); return UUID.randomUUID().toString(); } } } 

我的testing课:

 @RunWith(PowerMockRunner.class) @PrepareForTest(SmokeRouteBuilder.class) public class SmokeRouteBuilderTest { @Test public void testSmokeMessageId_exception() throws UnknownHostException { UUID id = UUID.randomUUID(); mockStatic(InetAddress.class); mockStatic(UUID.class); when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class); when(UUID.randomUUID()).thenReturn(id); assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId()); } } 

你可以做一些重构:

 public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory { @Override public Connection getConnection() { try { return _getConnection(...some params...); } catch (SQLException e) { throw new RuntimeException(e); } } //method to forward parameters, enabling mocking, extension, etc Connection _getConnection(...some params...) throws SQLException { return DriverManager.getConnection(...some params...); } } 

然后你可以扩展你的类MySQLDatabaseConnectionFactory来返回一个模拟连接,对参数做断言等等。

扩展类可以驻留在testing用例中,如果它位于相同的包中(我鼓励你这样做)

 public class MockedConnectionFactory extends MySQLDatabaseConnectionFactory { Connection _getConnection(...some params...) throws SQLException { if (some param != something) throw new InvalidParameterException(); //consider mocking some methods with when(yourMock.something()).thenReturn(value) return Mockito.mock(Connection.class); } } 

观察:当您在静态实体中调用静态方法时,您需要更改@PrepareForTest中的类。

例如:

 securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM); 

对于上面的代码,如果你需要模拟MessageDigest类,请使用

 @PrepareForTest(MessageDigest.class) 

而如果你有如下的东西:

 public class CustomObjectRule { object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM) .digest(message.getBytes(ENCODING))); } 

那么,你需要准备这个代码所在的类。

 @PrepareForTest(CustomObjectRule.class) 

然后嘲笑这个方法:

 PowerMockito.mockStatic(MessageDigest.class); PowerMockito.when(MessageDigest.getInstance(Mockito.anyString())) .thenThrow(new RuntimeException()); 

我也写了一个Mockito和AspectJ的组合: https : //github.com/iirekm/misc/tree/master/ajmock

你的例子变成:

 when(() -> DriverManager.getConnection(...)).thenReturn(...);