我写了一个工厂来产生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。是否有一个好的方法来模拟/验证这个特定的用例?
从Mockito 3.4.0开始,就可以在Mockito中模拟静态方法。
详情见:
https://github.com/mockito/mockito/releases/tag/v3.4.0
https://github.com/mockito/mockito/issues/1013
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks
assertEquals("foo", Foo.method());
try (MockedStatic mocked = mockStatic(Foo.class)) {
mocked.when(Foo::method).thenReturn("bar");
assertEquals("bar", Foo.method());
mocked.verify(Foo::method);
}
assertEquals("foo", Foo.method());
在你的例子中,是这样的:
@Test
public void testStaticMockWithVerification() throws SQLException {
try (MockedStatic<DriverManager> dummy = Mockito.mockStatic(DriverManager.class)) {
DatabaseConnectionFactory factory = new MySQLDatabaseConnectionFactory();
dummy.when(() -> DriverManager.getConnection("arg1", "arg2", "arg3"))
.thenReturn(new Connection() {/*...*/});
factory.getConnection();
dummy.verify(() -> DriverManager.getConnection(eq("arg1"), eq("arg2"), eq("arg3")));
}
}
注意:模拟STATIC METHODS需要模拟内联依赖而不是模拟核心。
对于JUnit5也添加这个:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
当您尝试模拟静态方法时,必须在try块中编写测试。因为重要的是要注意,作用域模拟必须由激活模拟的实体关闭。
try (MockedStatic<Tester> tester = Mockito.mockStatic(Tester.class)) {
tester.when(() -> Tester.testStatic("Testing..")).thenReturn(mock(ReturnObject.class));
//Here you have to write the test cases
}
在上面的例子中,我们必须模拟测试器类testStatic方法,输入参数为“Testing…”。在这里,该方法将返回一个ReturnObject类类型对象。因此我们写mockito当链像上面。
不要忘记在你的Gradle/maven中添加以下依赖项
testImplementation 'org.mockito:mockito-inline:4.3.1'
避免使用无法避免的静态方法的典型策略是创建包装对象并使用包装对象。
包装器对象成为真正静态类的外观,您不需要测试它们。
包装器对象可以是这样的
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,将它作为协作器注入(甚至不需要复杂的构造函数进行测试),然后继续模拟。