java - method - powermock maven
Métodos estáticos de burla con PowerMock y Mockito (1)
Estoy intentando verificar una llamada a java.sql.DriverManager.getConnection
utilizando JUnit, Mockito y PowerMock.
Aquí está mi caso de prueba:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class MySQLDatabaseConnectionFactoryTest {
private ConfigurationService configurationService;
private MySQLDatabaseConnectionFactory reference;
@Before
public void setUp() throws Exception {
this.reference = new MySQLDatabaseConnectionFactory();
}
@Test
public void testGetConnection() throws SQLException {
// setup
Connection connection = mock(Connection.class);
PowerMockito.mockStatic(DriverManager.class);
when(DriverManager.getConnection(anyString(), anyString(), anyString())).thenReturn(connection);
// run
this.reference.getConnection();
// verify
PowerMockito.verifyStatic();
DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
}
}
Aquí está el código bajo prueba:
public class MySQLDatabaseConnectionFactory implements
DatabaseConnectionFactory {
@Override
public Connection getConnection(IApplicationInstance appInstance) {
try {
return DriverManager.getConnection(String.format("jdbc:mysql://%s:%d/%s",
MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE), MYSQL_USERNAME, MYSQL_PASSWORD);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Curiosamente, este código falla con una java.sql.SQLException
:
java.lang.RuntimeException: java.sql.SQLException: No suitable driver found for jdbc:mysql://myhost:1111/database
Ahora, puedo asegurarme fácilmente de que mi controlador SQL (MySQL en este caso) está cargado en el momento de la prueba, pero ¿por qué no está completamente burlado el método estático sin efectos secundarios?
Actualizar:
Mejor he aislado el problema. He agregado un método de prueba a mi caso de prueba que intenta obtener una conexión de DriverManager
:
@Test
public void testSomething() {
Connection conn = mock(Connection.class);
mockStatic(DriverManager.class);
when(DriverManager.getConnection(anyString())).thenReturn(conn);
Connection c = DriverManager.getConnection("whut");
verifyStatic();
DriverManager.getConnection("whut");
}
Esta prueba realmente pasa, mientras que la otra prueba todavía falla. Parece que PowerMock no se está burlando de la referencia a la clase dentro de MySQLDatabaseConnectionFactory
. ¿Cómo puedo solucionar esto?
Cambiar su valor de anotación @PrepareForTest
a MySQLDatabaseConnectionFactory.class
resolverá este problema.
Esta anotación le dice a PowerMock que prepare ciertas clases para la prueba. Las clases que deben definirse mediante esta anotación son, por lo general, aquellas que deben manipularse mediante códigos de bytes. Esto incluye clases finales, clases con final, privadas, estáticas.
En esta situación, PowerMockito debe reemplazar la invocación al método estático DriverManager.getConnection
con código simulado. Esto se hace con el uso de la manipulación de byte-code.
Codigo completo
@RunWith(PowerMockRunner.class)
@PrepareForTest(MySQLDatabaseConnectionFactory.class)
public class MySQLDatabaseConnectionFactoryTest {
private MySQLDatabaseConnectionFactory reference;
@Before
public void setUp() throws Exception {
reference = new MySQLDatabaseConnectionFactory();
}
@Test
public void testGetConnection() throws SQLException {
// given
PowerMockito.mockStatic(DriverManager.class);
BDDMockito.given(DriverManager.getConnection(anyString(), anyString(), anyString()))
.willReturn(mock(Connection.class));
// when
reference.getConnection();
// then
PowerMockito.verifyStatic();
DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
}
}
¡Gracias a @Szpak por ayudarme a resolver este problema!