Fix possible Mockito concurrency issue

This commit is contained in:
Christian Beikov 2023-02-06 12:17:59 +01:00
parent 3df56ac794
commit 563880037e
2 changed files with 25 additions and 16 deletions

View File

@ -10,6 +10,7 @@ import org.hibernate.c3p0.internal.C3P0ConnectionProvider;
import org.hibernate.testing.util.ReflectionUtil; import org.hibernate.testing.util.ReflectionUtil;
import org.mockito.Answers;
import org.mockito.MockSettings; import org.mockito.MockSettings;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -18,25 +19,24 @@ import org.mockito.Mockito;
*/ */
public class C3P0ProxyConnectionProvider extends C3P0ConnectionProvider { public class C3P0ProxyConnectionProvider extends C3P0ConnectionProvider {
private static final MockSettings VERIFIEABLE_MOCK_SETTINGS = Mockito.withSettings()
.defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS );
private final Map<Connection, Connection> connectionSpyMap = new HashMap<>(); private final Map<Connection, Connection> connectionSpyMap = new HashMap<>();
private static <T> T spy(T subject) {
return Mockito.mock( (Class<T>) subject.getClass(), VERIFIEABLE_MOCK_SETTINGS.spiedInstance( subject ) );
}
@Override @Override
public void configure(Map<String, Object> props) { public void configure(Map<String, Object> props) {
super.configure( props ); super.configure( props );
DataSource ds = unwrap( DataSource.class ); DataSource ds = unwrap( DataSource.class );
DataSource dataSource = spy( ds ); DataSource dataSource = Mockito.mock(
DataSource.class,
Mockito.withSettings().defaultAnswer( Answers.CALLS_REAL_METHODS ).spiedInstance( ds )
);
try { try {
Mockito.doAnswer( invocation -> { Mockito.doAnswer( invocation -> {
Connection connection = (Connection) invocation.callRealMethod(); Connection connection = (Connection) invocation.callRealMethod();
Connection connectionSpy = spy( connection ); Connection connectionSpy = Mockito.mock(
Connection.class,
Mockito.withSettings().defaultAnswer( Answers.CALLS_REAL_METHODS ).spiedInstance( connection )
);
connectionSpyMap.put( connectionSpy, connection ); connectionSpyMap.put( connectionSpy, connection );
return connectionSpy; return connectionSpy;
} ).when( dataSource ).getConnection(); } ).when( dataSource ).getConnection();

View File

@ -36,11 +36,6 @@ import org.mockito.internal.util.MockUtil;
*/ */
public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDelegate { public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDelegate {
private static final MockSettings MOCK_SETTINGS = Mockito.withSettings()
.stubOnly() //important optimisation: uses far less memory, at tradeoff of mocked methods no longer being verifiable but we often don't need that.
.defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS );
private static final MockSettings VERIFIABLE_MOCK_SETTINGS = Mockito.withSettings()
.defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS );
// We must keep around the mocked connections, otherwise they are garbage collected and trigger finalizers // We must keep around the mocked connections, otherwise they are garbage collected and trigger finalizers
// Since we use CALLS_REAL_METHODS this might close underlying IO resources which makes other objects unusable // Since we use CALLS_REAL_METHODS this might close underlying IO resources which makes other objects unusable
private static final Queue<Object> MOCKS = new LinkedBlockingQueue<>(); private static final Queue<Object> MOCKS = new LinkedBlockingQueue<>();
@ -76,8 +71,22 @@ public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDe
public PreparedStatementSpyConnectionProvider(boolean allowMockVerificationOnStatements, boolean allowMockVerificationOnConnections, boolean forceSupportsAggressiveRelease) { public PreparedStatementSpyConnectionProvider(boolean allowMockVerificationOnStatements, boolean allowMockVerificationOnConnections, boolean forceSupportsAggressiveRelease) {
super(forceSupportsAggressiveRelease); super(forceSupportsAggressiveRelease);
this.settingsForStatements = allowMockVerificationOnStatements ? VERIFIABLE_MOCK_SETTINGS : MOCK_SETTINGS; this.settingsForStatements = allowMockVerificationOnStatements ?
this.settingsForConnections = allowMockVerificationOnConnections ? VERIFIABLE_MOCK_SETTINGS : MOCK_SETTINGS; getVerifiableMockSettings() :
getMockSettings();
this.settingsForConnections = allowMockVerificationOnConnections ?
getVerifiableMockSettings() :
getMockSettings();
}
private static MockSettings getMockSettings() {
return Mockito.withSettings()
.stubOnly() //important optimisation: uses far less memory, at tradeoff of mocked methods no longer being verifiable but we often don't need that.
.defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS );
}
private static MockSettings getVerifiableMockSettings() {
return Mockito.withSettings().defaultAnswer( org.mockito.Answers.CALLS_REAL_METHODS );
} }
protected Connection actualConnection() throws SQLException { protected Connection actualConnection() throws SQLException {