This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cayenne.git

commit 6bfad9e5a114e53c5b6f211a2b236d4d93d2ff28
Author: Andrus Adamchik <[email protected]>
AuthorDate: Wed May 13 15:29:51 2026 -0400

    removing obsolete test mock dependency
---
 cayenne/pom.xml                                    |  17 -
 .../cayenne/access/jdbc/BatchActionLockingIT.java  |  20 +-
 .../access/jdbc/JDBCResultIteratorTest.java        |  18 +-
 .../cayenne/access/types/BooleanTypeTest.java      |  14 +-
 .../apache/cayenne/access/types/EnumTypeTest.java  |  10 +-
 .../configuration/mock/MockDataSourceFactory.java  |   4 +-
 .../runtime/DefaultDbAdapterFactoryTest.java       |  16 +-
 .../runtime/JNDIDataSourceFactoryIT.java           |   6 +-
 .../apache/cayenne/unit/jdbc/TestConnection.java   | 227 ++++++++++
 .../apache/cayenne/unit/jdbc/TestDataSource.java   |  57 +++
 .../cayenne/unit/jdbc/TestPreparedStatement.java   | 137 ++++++
 .../apache/cayenne/unit/jdbc/TestResultSet.java    | 493 +++++++++++++++++++++
 .../apache/cayenne/unit/jdbc/TestStatement.java    | 105 +++++
 pom.xml                                            |   5 -
 14 files changed, 1056 insertions(+), 73 deletions(-)

diff --git a/cayenne/pom.xml b/cayenne/pom.xml
index 686fc8930..af6a7b05d 100644
--- a/cayenne/pom.xml
+++ b/cayenne/pom.xml
@@ -62,23 +62,6 @@
             <version>${project.version}</version>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>com.mockrunner</groupId>
-            <artifactId>mockrunner-jdbc</artifactId>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <!-- this one have old Xerces dependency that clashes with 
JDK's one -->
-                    <groupId>nekohtml</groupId>
-                    <artifactId>nekohtml</artifactId>
-                </exclusion>
-                <exclusion>
-                    <!-- don't use anything related to this, and this is an 
old version -->
-                    <groupId>ant</groupId>
-                    <artifactId>ant</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/access/jdbc/BatchActionLockingIT.java
 
b/cayenne/src/test/java/org/apache/cayenne/access/jdbc/BatchActionLockingIT.java
index e791f9714..2fead147d 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/access/jdbc/BatchActionLockingIT.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/access/jdbc/BatchActionLockingIT.java
@@ -19,8 +19,6 @@
 
 package org.apache.cayenne.access.jdbc;
 
-import com.mockrunner.jdbc.PreparedStatementResultSetHandler;
-import com.mockrunner.mock.jdbc.MockConnection;
 import org.apache.cayenne.access.DataNode;
 import org.apache.cayenne.access.MockOperationObserver;
 import org.apache.cayenne.access.OptimisticLockException;
@@ -33,7 +31,7 @@ import org.apache.cayenne.map.DbAttribute;
 import org.apache.cayenne.map.DbEntity;
 import org.apache.cayenne.map.EntityResolver;
 import org.apache.cayenne.query.DeleteBatchQuery;
-import org.apache.cayenne.runtime.CayenneRuntime;
+import org.apache.cayenne.unit.jdbc.TestConnection;
 import org.apache.cayenne.testdo.locking.SimpleLockingTestEntity;
 import org.apache.cayenne.unit.di.runtime.CayenneProjects;
 import org.apache.cayenne.unit.di.runtime.CayenneTestsEnv;
@@ -78,12 +76,8 @@ public class BatchActionLockingIT {
 
                DeleteBatchTranslator batchQueryBuilder = new 
DeleteBatchTranslator(batchQuery, adapter);
 
-               MockConnection mockConnection = new MockConnection();
-               PreparedStatementResultSetHandler 
preparedStatementResultSetHandler = mockConnection
-                               .getPreparedStatementResultSetHandler();
-               preparedStatementResultSetHandler.setExactMatch(false);
-               preparedStatementResultSetHandler.setCaseSensitive(false);
-               preparedStatementResultSetHandler.prepareUpdateCount("DELETE", 
1);
+               TestConnection mockConnection = new TestConnection();
+               mockConnection.prepareUpdateCount("DELETE", 1);
 
                boolean generatesKeys = false;
 
@@ -120,12 +114,8 @@ public class BatchActionLockingIT {
 
                DeleteBatchTranslator batchQueryBuilder = new 
DeleteBatchTranslator(batchQuery, adapter);
 
-               MockConnection mockConnection = new MockConnection();
-               PreparedStatementResultSetHandler 
preparedStatementResultSetHandler = mockConnection
-                               .getPreparedStatementResultSetHandler();
-               preparedStatementResultSetHandler.setExactMatch(false);
-               preparedStatementResultSetHandler.setCaseSensitive(false);
-               preparedStatementResultSetHandler.prepareUpdateCount("DELETE", 
0);
+               TestConnection mockConnection = new TestConnection();
+               mockConnection.prepareUpdateCount("DELETE", 0);
 
                boolean generatesKeys = false;
                DataNode node = new DataNode();
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/access/jdbc/JDBCResultIteratorTest.java
 
b/cayenne/src/test/java/org/apache/cayenne/access/jdbc/JDBCResultIteratorTest.java
index 4fc83d6bb..f579764d7 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/access/jdbc/JDBCResultIteratorTest.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/access/jdbc/JDBCResultIteratorTest.java
@@ -19,9 +19,6 @@
 
 package org.apache.cayenne.access.jdbc;
 
-import com.mockrunner.mock.jdbc.MockConnection;
-import com.mockrunner.mock.jdbc.MockResultSet;
-import com.mockrunner.mock.jdbc.MockStatement;
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.access.jdbc.reader.DefaultRowReaderFactory;
 import org.apache.cayenne.access.jdbc.reader.RowReader;
@@ -29,6 +26,9 @@ import org.apache.cayenne.access.types.ExtendedTypeMap;
 import org.apache.cayenne.dba.DbAdapter;
 import org.apache.cayenne.map.ObjAttribute;
 import org.apache.cayenne.query.MockQueryMetadata;
+import org.apache.cayenne.unit.jdbc.TestConnection;
+import org.apache.cayenne.unit.jdbc.TestResultSet;
+import org.apache.cayenne.unit.jdbc.TestStatement;
 import org.junit.jupiter.api.Test;
 
 import java.sql.Connection;
@@ -45,9 +45,9 @@ public class JDBCResultIteratorTest {
 
        @Test
        public void nextDataRow() throws Exception {
-               Connection c = new MockConnection();
-               Statement s = new MockStatement(c);
-               MockResultSet rs = new MockResultSet("rs");
+               Connection c = new TestConnection();
+               Statement s = new TestStatement(c);
+               TestResultSet rs = new TestResultSet("rs");
                rs.addColumn("a", new Object[] { "1", "2", "3" });
 
                RowDescriptor descriptor = new 
RowDescriptorBuilder().setResultSet(rs).getDescriptor(new ExtendedTypeMap());
@@ -65,9 +65,9 @@ public class JDBCResultIteratorTest {
 
        @Test
        public void close() throws Exception {
-               Connection c = new MockConnection();
-               MockStatement s = new MockStatement(c);
-               MockResultSet rs = new MockResultSet("rs");
+               Connection c = new TestConnection();
+               TestStatement s = new TestStatement(c);
+               TestResultSet rs = new TestResultSet("rs");
                rs.addColumn("a", new Object[] { "1", "2", "3" });
 
                RowReader<?> rowReader = mock(RowReader.class);
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/access/types/BooleanTypeTest.java 
b/cayenne/src/test/java/org/apache/cayenne/access/types/BooleanTypeTest.java
index c8e402d85..b5a1eb76e 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/types/BooleanTypeTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/types/BooleanTypeTest.java
@@ -19,10 +19,9 @@
 
 package org.apache.cayenne.access.types;
 
-import java.sql.SQLException;
 import java.sql.Types;
 
-import com.mockrunner.mock.jdbc.MockResultSet;
+import org.apache.cayenne.unit.jdbc.TestResultSet;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -41,13 +40,10 @@ public class BooleanTypeTest {
 
     @Test
     public void materializeObjectFromResultSet() throws Exception {
-        MockResultSet rs = new MockResultSet("") {
-
-            @Override
-            public boolean getBoolean(int i) throws SQLException {
-                return (i + 2) % 2 == 0;
-            }
-        };
+        TestResultSet rs = new TestResultSet("");
+        rs.addColumn("odd", new Object[]{Boolean.FALSE});
+        rs.addColumn("even", new Object[]{Boolean.TRUE});
+        rs.next();
 
         BooleanType type = new BooleanType();
 
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/access/types/EnumTypeTest.java 
b/cayenne/src/test/java/org/apache/cayenne/access/types/EnumTypeTest.java
index 2d9a07630..c3c228d35 100644
--- a/cayenne/src/test/java/org/apache/cayenne/access/types/EnumTypeTest.java
+++ b/cayenne/src/test/java/org/apache/cayenne/access/types/EnumTypeTest.java
@@ -19,7 +19,7 @@
 
 package org.apache.cayenne.access.types;
 
-import com.mockrunner.mock.jdbc.MockResultSet;
+import org.apache.cayenne.unit.jdbc.TestResultSet;
 import org.junit.jupiter.api.Test;
 
 import java.sql.Types;
@@ -55,7 +55,7 @@ public class EnumTypeTest {
     public void materializeStringObject() throws Exception {
         EnumType type = new EnumType(MockEnum.class);
         
-        MockResultSet rs = new MockResultSet("Test");
+        TestResultSet rs = new TestResultSet("Test");
         rs.addColumn("Enum");
         rs.addRow(new Object[] {"b"});
         rs.next();
@@ -68,7 +68,7 @@ public class EnumTypeTest {
     public void materializeNumericObject() throws Exception {
         EnumType type = new EnumType(MockEnum.class);
         
-        MockResultSet rs = new MockResultSet("Test");
+        TestResultSet rs = new TestResultSet("Test");
         rs.addColumn("Enum");
         rs.addRow(new Object[] {2});
         rs.next();
@@ -81,7 +81,7 @@ public class EnumTypeTest {
     public void materializeStringObjectInnerEnum() throws Exception {
         EnumType type = new EnumType(InnerEnumHolder.InnerEnum.class);
         
-        MockResultSet rs = new MockResultSet("Test");
+        TestResultSet rs = new TestResultSet("Test");
         rs.addColumn("Enum");
         rs.addRow(new Object[] {"b"});
         rs.next();
@@ -94,7 +94,7 @@ public class EnumTypeTest {
     public void materializeNumericObjectInnerEnum() throws Exception {
         EnumType type = new EnumType(InnerEnumHolder.InnerEnum.class);
         
-        MockResultSet rs = new MockResultSet("Test");
+        TestResultSet rs = new TestResultSet("Test");
         rs.addColumn("Enum");
         rs.addRow(new Object[] {2});
         rs.next();
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/configuration/mock/MockDataSourceFactory.java
 
b/cayenne/src/test/java/org/apache/cayenne/configuration/mock/MockDataSourceFactory.java
index 3baf7cb9e..ff9a5247a 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/configuration/mock/MockDataSourceFactory.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/configuration/mock/MockDataSourceFactory.java
@@ -23,11 +23,11 @@ import javax.sql.DataSource;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
 import org.apache.cayenne.configuration.runtime.DataSourceFactory;
 
-import com.mockrunner.mock.jdbc.MockDataSource;
+import org.apache.cayenne.unit.jdbc.TestDataSource;
 
 public class MockDataSourceFactory implements DataSourceFactory {
 
     public DataSource getDataSource(DataNodeDescriptor nodeDescriptor) {
-        return new MockDataSource();
+        return new TestDataSource();
     }
 }
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DefaultDbAdapterFactoryTest.java
 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DefaultDbAdapterFactoryTest.java
index 86a0ae50a..75cf74369 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DefaultDbAdapterFactoryTest.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/DefaultDbAdapterFactoryTest.java
@@ -18,8 +18,6 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.runtime;
 
-import com.mockrunner.mock.jdbc.MockConnection;
-import com.mockrunner.mock.jdbc.MockDataSource;
 import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory;
 import org.apache.cayenne.access.types.DefaultValueObjectTypeRegistry;
 import org.apache.cayenne.access.types.ValueObjectTypeRegistry;
@@ -48,6 +46,8 @@ import 
org.apache.cayenne.reflect.generic.DefaultValueComparisonStrategyFactory;
 import org.apache.cayenne.reflect.generic.ValueComparisonStrategyFactory;
 import org.apache.cayenne.resource.ClassLoaderResourceLocator;
 import org.apache.cayenne.resource.ResourceLocator;
+import org.apache.cayenne.unit.jdbc.TestConnection;
+import org.apache.cayenne.unit.jdbc.TestDataSource;
 import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
@@ -70,9 +70,9 @@ public class DefaultDbAdapterFactoryTest {
         List<DbAdapterDetector> detectors = new ArrayList<>();
         detectors.add(md -> adapter);
 
-        MockConnection connection = new MockConnection();
+        TestConnection connection = new TestConnection();
 
-        MockDataSource dataSource = new MockDataSource();
+        TestDataSource dataSource = new TestDataSource();
         dataSource.setupConnection(connection);
 
         Module testModule = binder -> {
@@ -124,7 +124,7 @@ public class DefaultDbAdapterFactoryTest {
         DefaultDbAdapterFactory factory = new 
DefaultDbAdapterFactory(detectors);
         injector.injectMembers(factory);
 
-        DbAdapter createdAdapter = factory.createAdapter(new 
DataNodeDescriptor(), new MockDataSource());
+        DbAdapter createdAdapter = factory.createAdapter(new 
DataNodeDescriptor(), new TestDataSource());
         assertNotNull(createdAdapter);
         assertTrue(createdAdapter instanceof AutoAdapter, "Unexpected class: " 
+ createdAdapter.getClass().getName());
         assertEquals("CREATE TABLE Test ()", createdAdapter.createTable(new 
DbEntity("Test")));
@@ -160,7 +160,7 @@ public class DefaultDbAdapterFactoryTest {
         DefaultDbAdapterFactory factory = new 
DefaultDbAdapterFactory(detectors);
         injector.injectMembers(factory);
 
-        DbAdapter createdAdapter = factory.createAdapter(nodeDescriptor, new 
MockDataSource());
+        DbAdapter createdAdapter = factory.createAdapter(nodeDescriptor, new 
TestDataSource());
         assertNotNull(createdAdapter);
         assertTrue(createdAdapter instanceof SybaseAdapter, "Unexpected class: 
" + createdAdapter.getClass().getName());
     }
@@ -175,9 +175,9 @@ public class DefaultDbAdapterFactoryTest {
         List<DbAdapterDetector> detectors = new ArrayList<>();
         detectors.add(md -> adapter);
 
-        MockConnection connection = new MockConnection();
+        TestConnection connection = new TestConnection();
 
-        MockDataSource dataSource = new MockDataSource();
+        TestDataSource dataSource = new TestDataSource();
         dataSource.setupConnection(connection);
 
         Module testModule = binder -> {
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/JNDIDataSourceFactoryIT.java
 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/JNDIDataSourceFactoryIT.java
index 85a114888..5f107308e 100644
--- 
a/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/JNDIDataSourceFactoryIT.java
+++ 
b/cayenne/src/test/java/org/apache/cayenne/configuration/runtime/JNDIDataSourceFactoryIT.java
@@ -18,8 +18,8 @@
  ****************************************************************/
 package org.apache.cayenne.configuration.runtime;
 
-import com.mockrunner.mock.jdbc.MockDataSource;
 import org.apache.cayenne.configuration.DataNodeDescriptor;
+import org.apache.cayenne.unit.jdbc.TestDataSource;
 import org.apache.cayenne.di.Injector;
 import org.apache.cayenne.unit.di.runtime.CayenneProjects;
 import org.apache.cayenne.unit.di.runtime.CayenneTestsEnv;
@@ -45,7 +45,7 @@ public class JNDIDataSourceFactoryIT {
 
         JNDISetup.doSetup();
 
-        MockDataSource dataSource = new MockDataSource();
+        TestDataSource dataSource = new TestDataSource();
         InitialContext context = new InitialContext();
         context.bind(descriptor.getParameters(), dataSource);
 
@@ -69,7 +69,7 @@ public class JNDIDataSourceFactoryIT {
 
         JNDISetup.doSetup();
 
-        MockDataSource dataSource = new MockDataSource();
+        TestDataSource dataSource = new TestDataSource();
         InitialContext context = new InitialContext();
         context.bind("java:comp/env/" + descriptor.getParameters(), 
dataSource);
 
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestConnection.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestConnection.java
new file mode 100644
index 000000000..3411ca2bf
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestConnection.java
@@ -0,0 +1,227 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.unit.jdbc;
+
+import java.lang.reflect.Proxy;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+/**
+ * Minimal JDBC {@link Connection} stub for tests. Tracks commits/rollbacks and
+ * returns {@link TestPreparedStatement}s whose {@code executeUpdate()} returns
+ * a value pre-configured via {@link #prepareUpdateCount(String, int)}.
+ */
+public class TestConnection implements Connection {
+
+    private final List<UpdateCountRule> rules = new ArrayList<>();
+    private int numberCommits;
+    private int numberRollbacks;
+    private boolean autoCommit = true;
+    private boolean closed;
+
+    /**
+     * Registers an updateCount to be returned by {@link 
PreparedStatement#executeUpdate()}
+     * when the prepared SQL contains the given fragment (case-insensitive).
+     */
+    public void prepareUpdateCount(String sqlFragment, int updateCount) {
+        rules.add(new UpdateCountRule(sqlFragment, updateCount));
+    }
+
+    public int getNumberCommits() {
+        return numberCommits;
+    }
+
+    public int getNumberRollbacks() {
+        return numberRollbacks;
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql) {
+        return new TestPreparedStatement(this, sql, updateCountFor(sql));
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql, int 
autoGeneratedKeys) {
+        return prepareStatement(sql);
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) 
{
+        return prepareStatement(sql);
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql, String[] 
columnNames) {
+        return prepareStatement(sql);
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql, int resultSetType, 
int resultSetConcurrency) {
+        return prepareStatement(sql);
+    }
+
+    @Override
+    public PreparedStatement prepareStatement(String sql, int resultSetType, 
int resultSetConcurrency, int resultSetHoldability) {
+        return prepareStatement(sql);
+    }
+
+    @Override
+    public Statement createStatement() {
+        return new TestStatement(this);
+    }
+
+    @Override
+    public Statement createStatement(int resultSetType, int 
resultSetConcurrency) {
+        return createStatement();
+    }
+
+    @Override
+    public Statement createStatement(int resultSetType, int 
resultSetConcurrency, int resultSetHoldability) {
+        return createStatement();
+    }
+
+    @Override
+    public void commit() {
+        numberCommits++;
+    }
+
+    @Override
+    public void rollback() {
+        numberRollbacks++;
+    }
+
+    @Override
+    public void rollback(Savepoint savepoint) {
+        numberRollbacks++;
+    }
+
+    @Override
+    public boolean getAutoCommit() {
+        return autoCommit;
+    }
+
+    @Override
+    public void setAutoCommit(boolean autoCommit) {
+        this.autoCommit = autoCommit;
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closed;
+    }
+
+    private int updateCountFor(String sql) {
+        String lower = sql == null ? "" : sql.toLowerCase();
+        for (UpdateCountRule rule : rules) {
+            if (lower.contains(rule.fragment.toLowerCase())) {
+                return rule.updateCount;
+            }
+        }
+        return 0;
+    }
+
+    private record UpdateCountRule(String fragment, int updateCount) {}
+
+    // --- everything below is unused by tests ---
+
+    private static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException("not implemented in 
TestConnection");
+    }
+
+    @Override public CallableStatement prepareCall(String sql) { throw uoe(); }
+    @Override public CallableStatement prepareCall(String sql, int 
resultSetType, int resultSetConcurrency) { throw uoe(); }
+    @Override public CallableStatement prepareCall(String sql, int 
resultSetType, int resultSetConcurrency, int resultSetHoldability) { throw 
uoe(); }
+    @Override public String nativeSQL(String sql) { return sql; }
+    @Override public DatabaseMetaData getMetaData() { return stubMetaData(); }
+
+    private DatabaseMetaData stubMetaData() {
+        return (DatabaseMetaData) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{DatabaseMetaData.class},
+                (proxy, method, args) -> {
+                    Class<?> rt = method.getReturnType();
+                    if (rt == boolean.class) return false;
+                    if (rt == int.class || rt == short.class || rt == 
byte.class) return 0;
+                    if (rt == long.class) return 0L;
+                    if (rt == float.class) return 0f;
+                    if (rt == double.class) return 0d;
+                    if (rt == ResultSet.class) return new TestResultSet("");
+                    if (rt == String.class) return "";
+                    if (rt == Connection.class) return this;
+                    return null;
+                });
+    }
+    @Override public void setReadOnly(boolean readOnly) {}
+    @Override public boolean isReadOnly() { return false; }
+    @Override public void setCatalog(String catalog) {}
+    @Override public String getCatalog() { return null; }
+    @Override public void setTransactionIsolation(int level) {}
+    @Override public int getTransactionIsolation() { return TRANSACTION_NONE; }
+    @Override public SQLWarning getWarnings() { return null; }
+    @Override public void clearWarnings() {}
+    @Override public Map<String, Class<?>> getTypeMap() { throw uoe(); }
+    @Override public void setTypeMap(Map<String, Class<?>> map) {}
+    @Override public void setHoldability(int holdability) {}
+    @Override public int getHoldability() { return 0; }
+    @Override public Savepoint setSavepoint() { throw uoe(); }
+    @Override public Savepoint setSavepoint(String name) { throw uoe(); }
+    @Override public void releaseSavepoint(Savepoint savepoint) {}
+    @Override public Clob createClob() { throw uoe(); }
+    @Override public Blob createBlob() { throw uoe(); }
+    @Override public NClob createNClob() { throw uoe(); }
+    @Override public SQLXML createSQLXML() { throw uoe(); }
+    @Override public boolean isValid(int timeout) { return !closed; }
+    @Override public void setClientInfo(String name, String value) throws 
SQLClientInfoException {}
+    @Override public void setClientInfo(Properties properties) throws 
SQLClientInfoException {}
+    @Override public String getClientInfo(String name) { return null; }
+    @Override public Properties getClientInfo() { return new Properties(); }
+    @Override public Array createArrayOf(String typeName, Object[] elements) { 
throw uoe(); }
+    @Override public Struct createStruct(String typeName, Object[] attributes) 
{ throw uoe(); }
+    @Override public void setSchema(String schema) {}
+    @Override public String getSchema() { return null; }
+    @Override public void abort(Executor executor) {}
+    @Override public void setNetworkTimeout(Executor executor, int 
milliseconds) {}
+    @Override public int getNetworkTimeout() { return 0; }
+    @Override public <T> T unwrap(Class<T> iface) throws SQLException { throw 
new SQLException("not supported"); }
+    @Override public boolean isWrapperFor(Class<?> iface) { return false; }
+}
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestDataSource.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestDataSource.java
new file mode 100644
index 000000000..204aa7fcb
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestDataSource.java
@@ -0,0 +1,57 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.unit.jdbc;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+/**
+ * Minimal JDBC {@link DataSource} stub for tests. Returns a configurable
+ * {@link Connection} (default: a fresh {@link TestConnection}) from {@link 
#getConnection()}.
+ */
+public class TestDataSource implements DataSource {
+
+    private Connection connection = new TestConnection();
+
+    public void setupConnection(Connection connection) {
+        this.connection = connection;
+    }
+
+    @Override
+    public Connection getConnection() {
+        return connection;
+    }
+
+    @Override
+    public Connection getConnection(String username, String password) {
+        return connection;
+    }
+
+    @Override public PrintWriter getLogWriter() { return null; }
+    @Override public void setLogWriter(PrintWriter out) {}
+    @Override public void setLoginTimeout(int seconds) {}
+    @Override public int getLoginTimeout() { return 0; }
+    @Override public Logger getParentLogger() { return 
Logger.getLogger("global"); }
+    @Override public <T> T unwrap(Class<T> iface) throws SQLException { throw 
new SQLException("not supported"); }
+    @Override public boolean isWrapperFor(Class<?> iface) { return false; }
+}
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestPreparedStatement.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestPreparedStatement.java
new file mode 100644
index 000000000..d57fb62d1
--- /dev/null
+++ 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestPreparedStatement.java
@@ -0,0 +1,137 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.unit.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+/**
+ * Minimal JDBC {@link PreparedStatement} stub for tests.
+ * Returns a pre-configured updateCount from {@link #executeUpdate()}; all 
parameter
+ * bind methods are no-ops.
+ */
+public class TestPreparedStatement extends TestStatement implements 
PreparedStatement {
+
+    private final String sql;
+    private final int updateCount;
+
+    public TestPreparedStatement(Connection connection, String sql, int 
updateCount) {
+        super(connection);
+        this.sql = sql;
+        this.updateCount = updateCount;
+    }
+
+    public String getSql() {
+        return sql;
+    }
+
+    @Override
+    public int executeUpdate() {
+        return updateCount;
+    }
+
+    @Override
+    public boolean execute() {
+        return false;
+    }
+
+    @Override
+    public ResultSet executeQuery() {
+        return new TestResultSet(sql);
+    }
+
+    @Override
+    public ResultSet getGeneratedKeys() {
+        return new TestResultSet(sql);
+    }
+
+    // --- parameter bind no-ops ---
+
+    @Override public void setNull(int parameterIndex, int sqlType) {}
+    @Override public void setNull(int parameterIndex, int sqlType, String 
typeName) {}
+    @Override public void setBoolean(int parameterIndex, boolean x) {}
+    @Override public void setByte(int parameterIndex, byte x) {}
+    @Override public void setShort(int parameterIndex, short x) {}
+    @Override public void setInt(int parameterIndex, int x) {}
+    @Override public void setLong(int parameterIndex, long x) {}
+    @Override public void setFloat(int parameterIndex, float x) {}
+    @Override public void setDouble(int parameterIndex, double x) {}
+    @Override public void setBigDecimal(int parameterIndex, BigDecimal x) {}
+    @Override public void setString(int parameterIndex, String x) {}
+    @Override public void setBytes(int parameterIndex, byte[] x) {}
+    @Override public void setDate(int parameterIndex, Date x) {}
+    @Override public void setDate(int parameterIndex, Date x, Calendar cal) {}
+    @Override public void setTime(int parameterIndex, Time x) {}
+    @Override public void setTime(int parameterIndex, Time x, Calendar cal) {}
+    @Override public void setTimestamp(int parameterIndex, Timestamp x) {}
+    @Override public void setTimestamp(int parameterIndex, Timestamp x, 
Calendar cal) {}
+    @Override public void setAsciiStream(int parameterIndex, InputStream x, 
int length) {}
+    @Override public void setAsciiStream(int parameterIndex, InputStream x, 
long length) {}
+    @Override public void setAsciiStream(int parameterIndex, InputStream x) {}
+    @Override public void setUnicodeStream(int parameterIndex, InputStream x, 
int length) {}
+    @Override public void setBinaryStream(int parameterIndex, InputStream x, 
int length) {}
+    @Override public void setBinaryStream(int parameterIndex, InputStream x, 
long length) {}
+    @Override public void setBinaryStream(int parameterIndex, InputStream x) {}
+    @Override public void setCharacterStream(int parameterIndex, Reader 
reader, int length) {}
+    @Override public void setCharacterStream(int parameterIndex, Reader 
reader, long length) {}
+    @Override public void setCharacterStream(int parameterIndex, Reader 
reader) {}
+    @Override public void setNCharacterStream(int parameterIndex, Reader 
value, long length) {}
+    @Override public void setNCharacterStream(int parameterIndex, Reader 
value) {}
+    @Override public void setObject(int parameterIndex, Object x) {}
+    @Override public void setObject(int parameterIndex, Object x, int 
targetSqlType) {}
+    @Override public void setObject(int parameterIndex, Object x, int 
targetSqlType, int scaleOrLength) {}
+    @Override public void setRef(int parameterIndex, Ref x) {}
+    @Override public void setBlob(int parameterIndex, Blob x) {}
+    @Override public void setBlob(int parameterIndex, InputStream inputStream, 
long length) {}
+    @Override public void setBlob(int parameterIndex, InputStream inputStream) 
{}
+    @Override public void setClob(int parameterIndex, Clob x) {}
+    @Override public void setClob(int parameterIndex, Reader reader, long 
length) {}
+    @Override public void setClob(int parameterIndex, Reader reader) {}
+    @Override public void setNClob(int parameterIndex, NClob value) {}
+    @Override public void setNClob(int parameterIndex, Reader reader, long 
length) {}
+    @Override public void setNClob(int parameterIndex, Reader reader) {}
+    @Override public void setArray(int parameterIndex, Array x) {}
+    @Override public void setURL(int parameterIndex, URL x) {}
+    @Override public void setRowId(int parameterIndex, RowId x) {}
+    @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) {}
+    @Override public void setNString(int parameterIndex, String value) {}
+    @Override public void addBatch() {}
+    @Override public void clearParameters() {}
+
+    @Override public ResultSetMetaData getMetaData() { throw uoe(); }
+    @Override public ParameterMetaData getParameterMetaData() { throw uoe(); }
+}
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestResultSet.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestResultSet.java
new file mode 100644
index 000000000..a9e54e8e6
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestResultSet.java
@@ -0,0 +1,493 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.unit.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Minimal in-memory JDBC {@link ResultSet} for tests. Replaces the parts of
+ * {@code com.mockrunner.mock.jdbc.MockResultSet} that Cayenne tests rely on.
+ *
+ * <p>Configure by calling {@link #addColumn(String)} or {@link 
#addColumn(String, Object[])}
+ * before reading. Then call {@link #next()} to advance the cursor.
+ */
+public class TestResultSet implements ResultSet {
+
+    private final String name;
+    private final List<String> columnNames = new ArrayList<>();
+    private final List<List<Object>> rows = new ArrayList<>();
+    private int cursor = -1;
+    private boolean closed;
+    private boolean lastWasNull;
+
+    public TestResultSet(String name) {
+        this.name = name;
+    }
+
+    public void addColumn(String columnName) {
+        columnNames.add(columnName);
+        // pad existing rows so their length matches column count
+        for (List<Object> row : rows) {
+            while (row.size() < columnNames.size()) {
+                row.add(null);
+            }
+        }
+    }
+
+    public void addColumn(String columnName, Object[] values) {
+        int colIdx = columnNames.size();
+        columnNames.add(columnName);
+        for (int i = 0; i < values.length; i++) {
+            ensureRow(i);
+            List<Object> row = rows.get(i);
+            while (row.size() <= colIdx) {
+                row.add(null);
+            }
+            row.set(colIdx, values[i]);
+        }
+    }
+
+    public void addRow(Object[] values) {
+        List<Object> row = new ArrayList<>(columnNames.size());
+        for (int i = 0; i < columnNames.size(); i++) {
+            row.add(i < values.length ? values[i] : null);
+        }
+        rows.add(row);
+    }
+
+    private void ensureRow(int index) {
+        while (rows.size() <= index) {
+            List<Object> row = new ArrayList<>(columnNames.size());
+            for (int i = 0; i < columnNames.size(); i++) {
+                row.add(null);
+            }
+            rows.add(row);
+        }
+    }
+
+    private Object value(int columnIndex) throws SQLException {
+        if (cursor < 0 || cursor >= rows.size()) {
+            throw new SQLException("No current row");
+        }
+        if (columnIndex < 1 || columnIndex > columnNames.size()) {
+            throw new SQLException("Invalid column index: " + columnIndex);
+        }
+        Object v = rows.get(cursor).get(columnIndex - 1);
+        lastWasNull = (v == null);
+        return v;
+    }
+
+    private int columnIndex(String columnLabel) throws SQLException {
+        for (int i = 0; i < columnNames.size(); i++) {
+            if (columnNames.get(i).equalsIgnoreCase(columnLabel)) {
+                return i + 1;
+            }
+        }
+        throw new SQLException("Unknown column: " + columnLabel);
+    }
+
+    @Override
+    public boolean next() {
+        cursor++;
+        return cursor < rows.size();
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closed;
+    }
+
+    @Override
+    public boolean wasNull() {
+        return lastWasNull;
+    }
+
+    @Override
+    public ResultSetMetaData getMetaData() {
+        return new TestResultSetMetaData(columnNames);
+    }
+
+    @Override
+    public Object getObject(int columnIndex) throws SQLException {
+        return value(columnIndex);
+    }
+
+    @Override
+    public Object getObject(String columnLabel) throws SQLException {
+        return getObject(columnIndex(columnLabel));
+    }
+
+    @Override
+    public String getString(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        return v == null ? null : v.toString();
+    }
+
+    @Override
+    public String getString(String columnLabel) throws SQLException {
+        return getString(columnIndex(columnLabel));
+    }
+
+    @Override
+    public boolean getBoolean(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        if (v == null) {
+            return false;
+        }
+        if (v instanceof Boolean b) {
+            return b;
+        }
+        if (v instanceof Number n) {
+            return n.intValue() != 0;
+        }
+        return Boolean.parseBoolean(v.toString());
+    }
+
+    @Override
+    public boolean getBoolean(String columnLabel) throws SQLException {
+        return getBoolean(columnIndex(columnLabel));
+    }
+
+    @Override
+    public int getInt(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        if (v == null) {
+            return 0;
+        }
+        if (v instanceof Number n) {
+            return n.intValue();
+        }
+        return Integer.parseInt(v.toString());
+    }
+
+    @Override
+    public int getInt(String columnLabel) throws SQLException {
+        return getInt(columnIndex(columnLabel));
+    }
+
+    @Override
+    public long getLong(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        if (v == null) {
+            return 0;
+        }
+        if (v instanceof Number n) {
+            return n.longValue();
+        }
+        return Long.parseLong(v.toString());
+    }
+
+    @Override
+    public long getLong(String columnLabel) throws SQLException {
+        return getLong(columnIndex(columnLabel));
+    }
+
+    @Override
+    public double getDouble(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        if (v == null) {
+            return 0;
+        }
+        if (v instanceof Number n) {
+            return n.doubleValue();
+        }
+        return Double.parseDouble(v.toString());
+    }
+
+    @Override
+    public double getDouble(String columnLabel) throws SQLException {
+        return getDouble(columnIndex(columnLabel));
+    }
+
+    @Override
+    public float getFloat(int columnIndex) throws SQLException {
+        return (float) getDouble(columnIndex);
+    }
+
+    @Override
+    public float getFloat(String columnLabel) throws SQLException {
+        return getFloat(columnIndex(columnLabel));
+    }
+
+    @Override
+    public short getShort(int columnIndex) throws SQLException {
+        return (short) getInt(columnIndex);
+    }
+
+    @Override
+    public short getShort(String columnLabel) throws SQLException {
+        return getShort(columnIndex(columnLabel));
+    }
+
+    @Override
+    public byte getByte(int columnIndex) throws SQLException {
+        return (byte) getInt(columnIndex);
+    }
+
+    @Override
+    public byte getByte(String columnLabel) throws SQLException {
+        return getByte(columnIndex(columnLabel));
+    }
+
+    @Override
+    public byte[] getBytes(int columnIndex) throws SQLException {
+        Object v = value(columnIndex);
+        return v == null ? null : (byte[]) v;
+    }
+
+    @Override
+    public byte[] getBytes(String columnLabel) throws SQLException {
+        return getBytes(columnIndex(columnLabel));
+    }
+
+    @Override public SQLWarning getWarnings() { return null; }
+    @Override public void clearWarnings() {}
+    @Override public String getCursorName() { return name; }
+    @Override public int findColumn(String columnLabel) throws SQLException { 
return columnIndex(columnLabel); }
+    @Override public boolean isBeforeFirst() { return cursor < 0; }
+    @Override public boolean isAfterLast() { return cursor >= rows.size(); }
+    @Override public boolean isFirst() { return cursor == 0; }
+    @Override public boolean isLast() { return cursor == rows.size() - 1; }
+    @Override public int getRow() { return cursor + 1; }
+    @Override public int getType() { return TYPE_FORWARD_ONLY; }
+    @Override public int getConcurrency() { return CONCUR_READ_ONLY; }
+    @Override public Statement getStatement() { return null; }
+    @Override public int getFetchDirection() { return FETCH_FORWARD; }
+    @Override public int getFetchSize() { return 0; }
+    @Override public int getHoldability() { return HOLD_CURSORS_OVER_COMMIT; }
+
+    // --- everything below is unused by tests; throw to flag accidental 
reliance ---
+
+    private static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException("not implemented in 
TestResultSet");
+    }
+
+    @Override public BigDecimal getBigDecimal(int columnIndex) { throw uoe(); }
+    @Override public BigDecimal getBigDecimal(String columnLabel) { throw 
uoe(); }
+    @Override public BigDecimal getBigDecimal(int columnIndex, int scale) { 
throw uoe(); }
+    @Override public BigDecimal getBigDecimal(String columnLabel, int scale) { 
throw uoe(); }
+    @Override public Date getDate(int columnIndex) { throw uoe(); }
+    @Override public Date getDate(String columnLabel) { throw uoe(); }
+    @Override public Date getDate(int columnIndex, Calendar cal) { throw 
uoe(); }
+    @Override public Date getDate(String columnLabel, Calendar cal) { throw 
uoe(); }
+    @Override public Time getTime(int columnIndex) { throw uoe(); }
+    @Override public Time getTime(String columnLabel) { throw uoe(); }
+    @Override public Time getTime(int columnIndex, Calendar cal) { throw 
uoe(); }
+    @Override public Time getTime(String columnLabel, Calendar cal) { throw 
uoe(); }
+    @Override public Timestamp getTimestamp(int columnIndex) { throw uoe(); }
+    @Override public Timestamp getTimestamp(String columnLabel) { throw uoe(); 
}
+    @Override public Timestamp getTimestamp(int columnIndex, Calendar cal) { 
throw uoe(); }
+    @Override public Timestamp getTimestamp(String columnLabel, Calendar cal) 
{ throw uoe(); }
+    @Override public InputStream getAsciiStream(int columnIndex) { throw 
uoe(); }
+    @Override public InputStream getAsciiStream(String columnLabel) { throw 
uoe(); }
+    @Override public InputStream getUnicodeStream(int columnIndex) { throw 
uoe(); }
+    @Override public InputStream getUnicodeStream(String columnLabel) { throw 
uoe(); }
+    @Override public InputStream getBinaryStream(int columnIndex) { throw 
uoe(); }
+    @Override public InputStream getBinaryStream(String columnLabel) { throw 
uoe(); }
+    @Override public Reader getCharacterStream(int columnIndex) { throw uoe(); 
}
+    @Override public Reader getCharacterStream(String columnLabel) { throw 
uoe(); }
+    @Override public Reader getNCharacterStream(int columnIndex) { throw 
uoe(); }
+    @Override public Reader getNCharacterStream(String columnLabel) { throw 
uoe(); }
+    @Override public String getNString(int columnIndex) { throw uoe(); }
+    @Override public String getNString(String columnLabel) { throw uoe(); }
+    @Override public Object getObject(int columnIndex, Map<String, Class<?>> 
map) { throw uoe(); }
+    @Override public Object getObject(String columnLabel, Map<String, 
Class<?>> map) { throw uoe(); }
+    @Override public <T> T getObject(int columnIndex, Class<T> type) { throw 
uoe(); }
+    @Override public <T> T getObject(String columnLabel, Class<T> type) { 
throw uoe(); }
+    @Override public Ref getRef(int columnIndex) { throw uoe(); }
+    @Override public Ref getRef(String columnLabel) { throw uoe(); }
+    @Override public Blob getBlob(int columnIndex) { throw uoe(); }
+    @Override public Blob getBlob(String columnLabel) { throw uoe(); }
+    @Override public Clob getClob(int columnIndex) { throw uoe(); }
+    @Override public Clob getClob(String columnLabel) { throw uoe(); }
+    @Override public NClob getNClob(int columnIndex) { throw uoe(); }
+    @Override public NClob getNClob(String columnLabel) { throw uoe(); }
+    @Override public Array getArray(int columnIndex) { throw uoe(); }
+    @Override public Array getArray(String columnLabel) { throw uoe(); }
+    @Override public URL getURL(int columnIndex) { throw uoe(); }
+    @Override public URL getURL(String columnLabel) { throw uoe(); }
+    @Override public RowId getRowId(int columnIndex) { throw uoe(); }
+    @Override public RowId getRowId(String columnLabel) { throw uoe(); }
+    @Override public SQLXML getSQLXML(int columnIndex) { throw uoe(); }
+    @Override public SQLXML getSQLXML(String columnLabel) { throw uoe(); }
+    @Override public void beforeFirst() { throw uoe(); }
+    @Override public void afterLast() { throw uoe(); }
+    @Override public boolean first() { throw uoe(); }
+    @Override public boolean last() { throw uoe(); }
+    @Override public boolean absolute(int row) { throw uoe(); }
+    @Override public boolean relative(int rows) { throw uoe(); }
+    @Override public boolean previous() { throw uoe(); }
+    @Override public void setFetchDirection(int direction) { throw uoe(); }
+    @Override public void setFetchSize(int rows) { throw uoe(); }
+    @Override public boolean rowUpdated() { throw uoe(); }
+    @Override public boolean rowInserted() { throw uoe(); }
+    @Override public boolean rowDeleted() { throw uoe(); }
+    @Override public void insertRow() { throw uoe(); }
+    @Override public void updateRow() { throw uoe(); }
+    @Override public void deleteRow() { throw uoe(); }
+    @Override public void refreshRow() { throw uoe(); }
+    @Override public void cancelRowUpdates() { throw uoe(); }
+    @Override public void moveToInsertRow() { throw uoe(); }
+    @Override public void moveToCurrentRow() { throw uoe(); }
+    @Override public void updateNull(int columnIndex) { throw uoe(); }
+    @Override public void updateNull(String columnLabel) { throw uoe(); }
+    @Override public void updateBoolean(int columnIndex, boolean x) { throw 
uoe(); }
+    @Override public void updateBoolean(String columnLabel, boolean x) { throw 
uoe(); }
+    @Override public void updateByte(int columnIndex, byte x) { throw uoe(); }
+    @Override public void updateByte(String columnLabel, byte x) { throw 
uoe(); }
+    @Override public void updateShort(int columnIndex, short x) { throw uoe(); 
}
+    @Override public void updateShort(String columnLabel, short x) { throw 
uoe(); }
+    @Override public void updateInt(int columnIndex, int x) { throw uoe(); }
+    @Override public void updateInt(String columnLabel, int x) { throw uoe(); }
+    @Override public void updateLong(int columnIndex, long x) { throw uoe(); }
+    @Override public void updateLong(String columnLabel, long x) { throw 
uoe(); }
+    @Override public void updateFloat(int columnIndex, float x) { throw uoe(); 
}
+    @Override public void updateFloat(String columnLabel, float x) { throw 
uoe(); }
+    @Override public void updateDouble(int columnIndex, double x) { throw 
uoe(); }
+    @Override public void updateDouble(String columnLabel, double x) { throw 
uoe(); }
+    @Override public void updateBigDecimal(int columnIndex, BigDecimal x) { 
throw uoe(); }
+    @Override public void updateBigDecimal(String columnLabel, BigDecimal x) { 
throw uoe(); }
+    @Override public void updateString(int columnIndex, String x) { throw 
uoe(); }
+    @Override public void updateString(String columnLabel, String x) { throw 
uoe(); }
+    @Override public void updateNString(int columnIndex, String x) { throw 
uoe(); }
+    @Override public void updateNString(String columnLabel, String x) { throw 
uoe(); }
+    @Override public void updateBytes(int columnIndex, byte[] x) { throw 
uoe(); }
+    @Override public void updateBytes(String columnLabel, byte[] x) { throw 
uoe(); }
+    @Override public void updateDate(int columnIndex, Date x) { throw uoe(); }
+    @Override public void updateDate(String columnLabel, Date x) { throw 
uoe(); }
+    @Override public void updateTime(int columnIndex, Time x) { throw uoe(); }
+    @Override public void updateTime(String columnLabel, Time x) { throw 
uoe(); }
+    @Override public void updateTimestamp(int columnIndex, Timestamp x) { 
throw uoe(); }
+    @Override public void updateTimestamp(String columnLabel, Timestamp x) { 
throw uoe(); }
+    @Override public void updateAsciiStream(int columnIndex, InputStream x, 
int length) { throw uoe(); }
+    @Override public void updateAsciiStream(String columnLabel, InputStream x, 
int length) { throw uoe(); }
+    @Override public void updateAsciiStream(int columnIndex, InputStream x, 
long length) { throw uoe(); }
+    @Override public void updateAsciiStream(String columnLabel, InputStream x, 
long length) { throw uoe(); }
+    @Override public void updateAsciiStream(int columnIndex, InputStream x) { 
throw uoe(); }
+    @Override public void updateAsciiStream(String columnLabel, InputStream x) 
{ throw uoe(); }
+    @Override public void updateBinaryStream(int columnIndex, InputStream x, 
int length) { throw uoe(); }
+    @Override public void updateBinaryStream(String columnLabel, InputStream 
x, int length) { throw uoe(); }
+    @Override public void updateBinaryStream(int columnIndex, InputStream x, 
long length) { throw uoe(); }
+    @Override public void updateBinaryStream(String columnLabel, InputStream 
x, long length) { throw uoe(); }
+    @Override public void updateBinaryStream(int columnIndex, InputStream x) { 
throw uoe(); }
+    @Override public void updateBinaryStream(String columnLabel, InputStream 
x) { throw uoe(); }
+    @Override public void updateCharacterStream(int columnIndex, Reader x, int 
length) { throw uoe(); }
+    @Override public void updateCharacterStream(String columnLabel, Reader x, 
int length) { throw uoe(); }
+    @Override public void updateCharacterStream(int columnIndex, Reader x, 
long length) { throw uoe(); }
+    @Override public void updateCharacterStream(String columnLabel, Reader x, 
long length) { throw uoe(); }
+    @Override public void updateCharacterStream(int columnIndex, Reader x) { 
throw uoe(); }
+    @Override public void updateCharacterStream(String columnLabel, Reader x) 
{ throw uoe(); }
+    @Override public void updateNCharacterStream(int columnIndex, Reader x, 
long length) { throw uoe(); }
+    @Override public void updateNCharacterStream(String columnLabel, Reader x, 
long length) { throw uoe(); }
+    @Override public void updateNCharacterStream(int columnIndex, Reader x) { 
throw uoe(); }
+    @Override public void updateNCharacterStream(String columnLabel, Reader x) 
{ throw uoe(); }
+    @Override public void updateObject(int columnIndex, Object x, int 
scaleOrLength) { throw uoe(); }
+    @Override public void updateObject(String columnLabel, Object x, int 
scaleOrLength) { throw uoe(); }
+    @Override public void updateObject(int columnIndex, Object x) { throw 
uoe(); }
+    @Override public void updateObject(String columnLabel, Object x) { throw 
uoe(); }
+    @Override public void updateRef(int columnIndex, Ref x) { throw uoe(); }
+    @Override public void updateRef(String columnLabel, Ref x) { throw uoe(); }
+    @Override public void updateBlob(int columnIndex, Blob x) { throw uoe(); }
+    @Override public void updateBlob(String columnLabel, Blob x) { throw 
uoe(); }
+    @Override public void updateBlob(int columnIndex, InputStream inputStream, 
long length) { throw uoe(); }
+    @Override public void updateBlob(String columnLabel, InputStream 
inputStream, long length) { throw uoe(); }
+    @Override public void updateBlob(int columnIndex, InputStream inputStream) 
{ throw uoe(); }
+    @Override public void updateBlob(String columnLabel, InputStream 
inputStream) { throw uoe(); }
+    @Override public void updateClob(int columnIndex, Clob x) { throw uoe(); }
+    @Override public void updateClob(String columnLabel, Clob x) { throw 
uoe(); }
+    @Override public void updateClob(int columnIndex, Reader reader, long 
length) { throw uoe(); }
+    @Override public void updateClob(String columnLabel, Reader reader, long 
length) { throw uoe(); }
+    @Override public void updateClob(int columnIndex, Reader reader) { throw 
uoe(); }
+    @Override public void updateClob(String columnLabel, Reader reader) { 
throw uoe(); }
+    @Override public void updateNClob(int columnIndex, NClob nClob) { throw 
uoe(); }
+    @Override public void updateNClob(String columnLabel, NClob nClob) { throw 
uoe(); }
+    @Override public void updateNClob(int columnIndex, Reader reader, long 
length) { throw uoe(); }
+    @Override public void updateNClob(String columnLabel, Reader reader, long 
length) { throw uoe(); }
+    @Override public void updateNClob(int columnIndex, Reader reader) { throw 
uoe(); }
+    @Override public void updateNClob(String columnLabel, Reader reader) { 
throw uoe(); }
+    @Override public void updateArray(int columnIndex, Array x) { throw uoe(); 
}
+    @Override public void updateArray(String columnLabel, Array x) { throw 
uoe(); }
+    @Override public void updateRowId(int columnIndex, RowId x) { throw uoe(); 
}
+    @Override public void updateRowId(String columnLabel, RowId x) { throw 
uoe(); }
+    @Override public void updateSQLXML(int columnIndex, SQLXML xmlObject) { 
throw uoe(); }
+    @Override public void updateSQLXML(String columnLabel, SQLXML xmlObject) { 
throw uoe(); }
+    @Override public <T> T unwrap(Class<T> iface) { throw uoe(); }
+    @Override public boolean isWrapperFor(Class<?> iface) { return false; }
+
+    private static final class TestResultSetMetaData implements 
ResultSetMetaData {
+        private final List<String> names;
+
+        TestResultSetMetaData(List<String> names) {
+            this.names = names;
+        }
+
+        @Override public int getColumnCount() { return names.size(); }
+        @Override public String getColumnLabel(int column) { return 
names.get(column - 1); }
+        @Override public String getColumnName(int column) { return 
names.get(column - 1); }
+        @Override public int getColumnType(int column) { return Types.OTHER; }
+        @Override public String getColumnTypeName(int column) { return 
"OTHER"; }
+        @Override public String getColumnClassName(int column) { return 
Object.class.getName(); }
+        @Override public int getPrecision(int column) { return 0; }
+        @Override public int getScale(int column) { return 0; }
+        @Override public int isNullable(int column) { return 
columnNullableUnknown; }
+        @Override public boolean isAutoIncrement(int column) { return false; }
+        @Override public boolean isCaseSensitive(int column) { return false; }
+        @Override public boolean isSearchable(int column) { return false; }
+        @Override public boolean isCurrency(int column) { return false; }
+        @Override public boolean isSigned(int column) { return false; }
+        @Override public int getColumnDisplaySize(int column) { return 0; }
+        @Override public String getSchemaName(int column) { return ""; }
+        @Override public String getTableName(int column) { return ""; }
+        @Override public String getCatalogName(int column) { return ""; }
+        @Override public boolean isReadOnly(int column) { return true; }
+        @Override public boolean isWritable(int column) { return false; }
+        @Override public boolean isDefinitelyWritable(int column) { return 
false; }
+        @Override public <T> T unwrap(Class<T> iface) throws SQLException { 
throw new SQLFeatureNotSupportedException(); }
+        @Override public boolean isWrapperFor(Class<?> iface) { return false; }
+    }
+}
diff --git 
a/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestStatement.java 
b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestStatement.java
new file mode 100644
index 000000000..d4de0ebae
--- /dev/null
+++ b/cayenne/src/test/java/org/apache/cayenne/unit/jdbc/TestStatement.java
@@ -0,0 +1,105 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.unit.jdbc;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+/**
+ * Minimal JDBC {@link Statement} stub for tests. Implements close/isClosed 
semantics
+ * and exposes the originating {@link Connection}.
+ */
+public class TestStatement implements Statement {
+
+    private final Connection connection;
+    private boolean closed;
+
+    public TestStatement() {
+        this(null);
+    }
+
+    public TestStatement(Connection connection) {
+        this.connection = connection;
+    }
+
+    @Override
+    public Connection getConnection() {
+        return connection;
+    }
+
+    @Override
+    public void close() {
+        closed = true;
+    }
+
+    @Override
+    public boolean isClosed() {
+        return closed;
+    }
+
+    // --- everything below is unused by tests ---
+
+    static UnsupportedOperationException uoe() {
+        return new UnsupportedOperationException("not implemented in 
TestStatement");
+    }
+
+    @Override public ResultSet executeQuery(String sql) { throw uoe(); }
+    @Override public int executeUpdate(String sql) { throw uoe(); }
+    @Override public int executeUpdate(String sql, int autoGeneratedKeys) { 
throw uoe(); }
+    @Override public int executeUpdate(String sql, int[] columnIndexes) { 
throw uoe(); }
+    @Override public int executeUpdate(String sql, String[] columnNames) { 
throw uoe(); }
+    @Override public boolean execute(String sql) { throw uoe(); }
+    @Override public boolean execute(String sql, int autoGeneratedKeys) { 
throw uoe(); }
+    @Override public boolean execute(String sql, int[] columnIndexes) { throw 
uoe(); }
+    @Override public boolean execute(String sql, String[] columnNames) { throw 
uoe(); }
+    @Override public int getMaxFieldSize() { return 0; }
+    @Override public void setMaxFieldSize(int max) {}
+    @Override public int getMaxRows() { return 0; }
+    @Override public void setMaxRows(int max) {}
+    @Override public void setEscapeProcessing(boolean enable) {}
+    @Override public int getQueryTimeout() { return 0; }
+    @Override public void setQueryTimeout(int seconds) {}
+    @Override public void cancel() {}
+    @Override public SQLWarning getWarnings() { return null; }
+    @Override public void clearWarnings() {}
+    @Override public void setCursorName(String name) {}
+    @Override public ResultSet getResultSet() { return null; }
+    @Override public int getUpdateCount() { return -1; }
+    @Override public boolean getMoreResults() { return false; }
+    @Override public boolean getMoreResults(int current) { return false; }
+    @Override public void setFetchDirection(int direction) {}
+    @Override public int getFetchDirection() { return ResultSet.FETCH_FORWARD; 
}
+    @Override public void setFetchSize(int rows) {}
+    @Override public int getFetchSize() { return 0; }
+    @Override public int getResultSetConcurrency() { return 
ResultSet.CONCUR_READ_ONLY; }
+    @Override public int getResultSetType() { return 
ResultSet.TYPE_FORWARD_ONLY; }
+    @Override public int getResultSetHoldability() { return 
ResultSet.HOLD_CURSORS_OVER_COMMIT; }
+    @Override public void addBatch(String sql) {}
+    @Override public void clearBatch() {}
+    @Override public int[] executeBatch() { return new int[0]; }
+    @Override public ResultSet getGeneratedKeys() { throw uoe(); }
+    @Override public void setPoolable(boolean poolable) {}
+    @Override public boolean isPoolable() { return false; }
+    @Override public void closeOnCompletion() {}
+    @Override public boolean isCloseOnCompletion() { return false; }
+    @Override public <T> T unwrap(Class<T> iface) { throw uoe(); }
+    @Override public boolean isWrapperFor(Class<?> iface) { return false; }
+}
diff --git a/pom.xml b/pom.xml
index 18945e6d2..94f0f5f35 100644
--- a/pom.xml
+++ b/pom.xml
@@ -373,11 +373,6 @@
                 <artifactId>jgoodies-looks</artifactId>
                 <version>2.7.0</version>
             </dependency>
-            <dependency>
-                <groupId>com.mockrunner</groupId>
-                <artifactId>mockrunner-jdbc</artifactId>
-                <version>2.0.7</version>
-            </dependency>
             <dependency>
                 <groupId>org.apache.velocity</groupId>
                 <artifactId>velocity-engine-core</artifactId>

Reply via email to