Search code examples
javamockitomockstatic

Mockito: mockStatic.when().thenThrow() behavior


I'm writing a test for a method that throws various exception. This test case is to throw SQLException and I noticed these behavior.

@Test
public void testGetPreparedStatement_throwsSQLException() throws Exception {
    try (MockedStatic<DriverManager> mockedDriverManager = mockStatic(DriverManager.class)) {
        // Throws org.mockito.exceptions.misusing.UnfinishedStubbingException
        mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
                .thenThrow(new SQLException("some message"));

        assertThrows(SQLException.class, () -> ((RequestDBServiceJDBC) RequestDBServiceJDBC.getInstance())
                .getPreparedStatement(new RequestCriteria()));
    }
}

// But somehow this works:
SQLException sqlException = new SQLException("some message");
mockedDriverManager.when(() -> DriverManager.getConnection(anyString(), anyString(), anyString()))
    .thenThrow(sqlException);

Error:

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at at com.devcenter.myproject.request.service.jdbc.RequestDBServiceJDBCTest.testGetPreparedStatement_throwsSQLException(RequestDBServiceJDBCTest.java:206)

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, which is not supported
 3. you are stubbing the behaviour of another mock inside before 'thenReturn' instruction is completed

    at java.sql/java.sql.DriverManager.getLogWriter(DriverManager.java:129)
    at java.sql/java.sql.SQLException.<init>(SQLException.java:125)
    at com.devcenter.myproject.request.service.jdbc.RequestDBServiceJDBCTest.testGetPreparedStatement_throwsSQLException(RequestDBServiceJDBCTest.java:207)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)

Is there any explanation for this?


Solution

  • SQLException constructor calls DriverManager.getLogWriter.

    It is not allowed to call methods of mocked class before you finished when().thenXXX() sequence.

    You have to construct exception before when called.

    Alternatively you can use .then(x -> { throw new SQLException("some message");}) instead of thenThrow.