Author: bayard Date: Tue Aug 16 05:51:37 2011 New Revision: 1158110 URL: http://svn.apache.org/viewvc?rev=1158110&view=rev Log: Applying William Speirs' patch to move QueryRunnerTest to Mockito (DBUTILS-78)
Modified: commons/proper/dbutils/trunk/src/test/org/apache/commons/dbutils/QueryRunnerTest.java Modified: commons/proper/dbutils/trunk/src/test/org/apache/commons/dbutils/QueryRunnerTest.java URL: http://svn.apache.org/viewvc/commons/proper/dbutils/trunk/src/test/org/apache/commons/dbutils/QueryRunnerTest.java?rev=1158110&r1=1158109&r2=1158110&view=diff ============================================================================== --- commons/proper/dbutils/trunk/src/test/org/apache/commons/dbutils/QueryRunnerTest.java (original) +++ commons/proper/dbutils/trunk/src/test/org/apache/commons/dbutils/QueryRunnerTest.java Tue Aug 16 05:51:37 2011 @@ -16,203 +16,476 @@ */ package org.apache.commons.dbutils; -import java.beans.IndexedPropertyDescriptor; -import java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Types; -import java.util.Arrays; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.RunnableFuture; + +import javax.sql.DataSource; -import junit.framework.TestCase; +import org.apache.commons.dbutils.handlers.ArrayHandler; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; -public class QueryRunnerTest extends TestCase { +public class QueryRunnerTest { QueryRunner runner; - PreparedStatement stmt; + ArrayHandler handler; + + @Mock DataSource dataSource; + @Mock Connection conn; + @Mock PreparedStatement stmt; + @Mock ParameterMetaData meta; + @Mock ResultSet results; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); // init the mocks + + when(dataSource.getConnection()).thenReturn(conn); + when(conn.prepareStatement(any(String.class))).thenReturn(stmt); + when(stmt.getParameterMetaData()).thenReturn(meta); + when(stmt.getResultSet()).thenReturn(results); + when(stmt.executeQuery()).thenReturn(results); + when(results.next()).thenReturn(false); + + handler = new ArrayHandler(); + runner = new QueryRunner(dataSource); + } + + // + // Batch test cases + // + + private void callGoodBatch(Connection conn, Object[][] params) throws Exception { + when(meta.getParameterCount()).thenReturn(2); + int[] ret = runner.batch(conn, "select * from blah where ? = ?", params); + + verify(stmt, times(2)).addBatch(); + verify(stmt, times(1)).executeBatch(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + } + + private void callGoodBatch(Object[][] params) throws Exception { + when(meta.getParameterCount()).thenReturn(2); + int[] ret = runner.batch("select * from blah where ? = ?", params); + + verify(stmt, times(2)).addBatch(); + verify(stmt, times(1)).executeBatch(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(1)).close(); // make sure we closed the connection + } + + @Test + public void testGoodBatch() throws Exception { + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + callGoodBatch(params); + } + + @Test + public void testGoodBatchPmdTrue() throws Exception { + runner = new QueryRunner(dataSource, true); + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + callGoodBatch(params); + } + + @Test + public void testGoodBatchDefaultConstructor() throws Exception { + runner = new QueryRunner(); + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + callGoodBatch(conn, params); + } + + @Test + public void testNullParamsBatch() throws Exception { + String[][] params = new String[][] { { null, "unit" }, { "test", null } }; + + callGoodBatch(params); + } + + + + // helper method for calling batch when an exception is expected + private void callBatchWithException(String sql, Object[][] params) throws Exception { + int[] ret = null; + boolean caught = false; + + try { + ret = runner.batch(sql, params); + + verify(stmt, times(2)).addBatch(); + verify(stmt, times(1)).executeBatch(); + verify(stmt, times(1)).close(); // make sure the statement is closed + verify(conn, times(1)).close(); // make sure the connection is closed + } catch(SQLException e) { + caught = true; + } + + if(!caught) + fail("Exception never thrown, but expected"); + } + + @Test + public void testTooFewParamsBatch() throws Exception { + String[][] params = new String[][] { { "unit" }, { "test" } }; + + callBatchWithException("select * from blah where ? = ?", params); + } + + @Test + public void testTooManyParamsBatch() throws Exception { + String[][] params = new String[][] { { "unit", "unit", "unit" }, { "test", "test", "test" } }; + + callBatchWithException("select * from blah where ? = ?", params); + } + + @Test(expected=SQLException.class) + public void testNullConnectionBatch() throws Exception { + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + when(meta.getParameterCount()).thenReturn(2); + when(dataSource.getConnection()).thenReturn(null); + + runner.batch("select * from blah where ? = ?", params); + } + + @Test(expected=SQLException.class) + public void testNullSqlBatch() throws Exception { + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + when(meta.getParameterCount()).thenReturn(2); + + runner.batch(null, params); + } + + @Test(expected=SQLException.class) + public void testNullParamsArgBatch() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + + runner.batch("select * from blah where ? = ?", null); + } + + @Test + public void testAddBatchException() throws Exception { + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + doThrow(new SQLException()).when(stmt).addBatch(); + + callBatchWithException("select * from blah where ? = ?", params); + } + + @Test + public void testExecuteBatchException() throws Exception { + String[][] params = new String[][] { { "unit", "unit" }, { "test", "test" } }; + + doThrow(new SQLException()).when(stmt).executeBatch(); + + callBatchWithException("select * from blah where ? = ?", params); + } + + + // + // Query test cases + // + private void callGoodQuery(Connection conn) throws Exception { + when(meta.getParameterCount()).thenReturn(2); + Object[] ret = runner.query(conn, "select * from blah where ? = ?", handler, "unit", "test"); + + verify(stmt, times(1)).executeQuery(); + verify(results, times(1)).close(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + + // call the other variation of query + when(meta.getParameterCount()).thenReturn(0); + ret = runner.query(conn, "select * from blah", handler); + + verify(stmt, times(2)).executeQuery(); + verify(results, times(2)).close(); + verify(stmt, times(2)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + } + + private void callGoodQuery() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + Object[] ret = runner.query("select * from blah where ? = ?", handler, "unit", "test"); + + verify(stmt, times(1)).executeQuery(); + verify(results, times(1)).close(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(1)).close(); // make sure we closed the connection + + // call the other variation of query + when(meta.getParameterCount()).thenReturn(0); + ret = runner.query("select * from blah", handler); + + verify(stmt, times(2)).executeQuery(); + verify(results, times(2)).close(); + verify(stmt, times(2)).close(); // make sure we closed the statement + verify(conn, times(2)).close(); // make sure we closed the connection + } + + @Test + public void testGoodQuery() throws Exception { + callGoodQuery(); + } + + @Test + public void testGoodQueryPmdTrue() throws Exception { + runner = new QueryRunner(true); + callGoodQuery(conn); + } + + @Test + public void testGoodQueryDefaultConstructor() throws Exception { + runner = new QueryRunner(); + callGoodQuery(conn); + } + + + // helper method for calling batch when an exception is expected + private void callQueryWithException(Object... params) throws Exception { + Object[] ret = null; + boolean caught = false; + + try { + when(meta.getParameterCount()).thenReturn(2); + ret = runner.query("select * from blah where ? = ?", handler, params); + + verify(stmt, never()).close(); // make sure the statement is still open + verify(stmt, times(1)).executeQuery(); + verify(results, times(1)).close(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(1)).close(); // make sure we closed the connection + } catch(SQLException e) { + caught = true; + } + + if(!caught) + fail("Exception never thrown, but expected"); + } + + @Test + public void testNoParamsQuery() throws Exception { + callQueryWithException(); + } + + @Test + public void testTooFewParamsQuery() throws Exception { + callQueryWithException("unit"); + } + + @Test + public void testTooManyParamsQuery() throws Exception { + callQueryWithException("unit", "test", "fail"); + } + + @Test(expected=SQLException.class) + public void testNullConnectionQuery() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + when(dataSource.getConnection()).thenReturn(null); + + runner.query("select * from blah where ? = ?", handler, "unit", "test"); + } + + @Test(expected=SQLException.class) + public void testNullSqlQuery() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + + runner.query(null, handler); + } + + @Test(expected=SQLException.class) + public void testNullHandlerQuery() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + + runner.query("select * from blah where ? = ?", null); + } + + @Test + public void testExecuteQueryException() throws Exception { + doThrow(new SQLException()).when(stmt).executeQuery(); + + callQueryWithException(handler, "unit", "test"); + } + + + // + // Update test cases + // + private void callGoodUpdate(Connection conn) throws Exception { + when(meta.getParameterCount()).thenReturn(2); + Integer ret = runner.update(conn, "update blah set ? = ?", "unit", "test"); + + verify(stmt, times(1)).executeUpdate(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + + // call the other variation + when(meta.getParameterCount()).thenReturn(0); + ret = runner.update(conn, "update blah set unit = test"); + + verify(stmt, times(2)).executeUpdate(); + verify(stmt, times(2)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + + // call the other variation + when(meta.getParameterCount()).thenReturn(1); + ret = runner.update(conn, "update blah set unit = ?", "test"); + + verify(stmt, times(3)).executeUpdate(); + verify(stmt, times(3)).close(); // make sure we closed the statement + verify(conn, times(0)).close(); // make sure we closed the connection + } - static final Method getParameterCount, getParameterType, getParameterMetaData; - static { - try { - getParameterCount = ParameterMetaData.class.getMethod("getParameterCount", new Class[0]); - getParameterType = ParameterMetaData.class.getMethod("getParameterType", new Class[]{int.class}); - getParameterMetaData = PreparedStatement.class.getMethod("getParameterMetaData", new Class[0]); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void setUp() { - runner = new QueryRunner(); - stmt = fakePreparedStatement(); - } - - public void testFillStatementWithNull() throws Exception { - stmt = fakeFillablePreparedStatement(false, new int[] {Types.VARCHAR, Types.BIGINT}); - runner.fillStatement(stmt, new Object[] { null, null }); - } - - public void testFillStatementWithNullOracle() throws Exception { - stmt = fakeFillablePreparedStatement(true, new int[] {Types.VARCHAR, Types.BIGINT}); - runner.fillStatement(stmt, new Object[] { null, null }); - } - - private PreparedStatement fakeFillablePreparedStatement(final boolean simulateOracle, final int[] types) throws NoSuchMethodException { - // prepare a mock ParameterMetaData and a mock PreparedStatement to return the PMD - final ParameterMetaData pmd = mockParameterMetaData(simulateOracle,types); - InvocationHandler stmtHandler = new InvocationHandler() { - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - if (getParameterMetaData.equals(method)) { - return pmd; - } - return null; - } - }; - return ProxyFactory.instance().createPreparedStatement(stmtHandler); - } - - private ParameterMetaData mockParameterMetaData(final boolean simulateOracle, final int[] types) { - InvocationHandler pmdHandler = new InvocationHandler() { - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - if (getParameterCount.equals(method)) { - return Integer.valueOf(types.length); - } - if (getParameterType.equals(method)) { - if (simulateOracle) throw new SQLException("Oracle fails when you call getParameterType"); - int arg = ((Integer)args[0]).intValue(); - return Integer.valueOf(types[arg-1]); - } - return null; - } - }; - - return (ParameterMetaData) Proxy.newProxyInstance( - pmdHandler.getClass().getClassLoader(), - new Class[] {ParameterMetaData.class}, - pmdHandler); - } - - public void testFillStatementWithBean() throws SQLException { - TestBean tb = new TestBean(); - tb.setOne("uno"); - tb.setTwo("dos"); - tb.setThree("tres"); - NoOpFillStatement fakeQueryRunner = new NoOpFillStatement(); - fakeQueryRunner.fillStatementWithBean(stmt, tb, new String[] {"three", "two", "one"}); - String[] expected = new String[] {"tres", "dos", "uno"}; - assertArrayEquals("Statement filled with incorrect parameters", expected, fakeQueryRunner.params); - } - - private PreparedStatement fakePreparedStatement() { - InvocationHandler noOpHandler = new InvocationHandler() { - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { - return null; - } - }; - PreparedStatement stmt = ProxyFactory.instance().createPreparedStatement(noOpHandler); - return stmt; - } - - public void testFillStatementWithBeanErrorNoReadMethod() throws Exception { - TestBean tb = new TestBean(); - PropertyDescriptor noReadMethod = new PropertyDescriptor("one", TestBean.class, null, "setOne"); - - PropertyDescriptor properties[] = new PropertyDescriptor[] { noReadMethod }; - try { - runner.fillStatementWithBean(stmt, tb, properties); - fail("Expected RuntimeException: tried to use a property with no read method"); - } catch (RuntimeException expected) {} - } - - public void testFillStatementWithBeanErrorBadReadMethod() throws Exception { - PropertyDescriptor badReadMethod = new IndexedPropertyDescriptor("indexed", getClass(), null, null, "getIndexed", null) { - @Override - public synchronized Method getReadMethod() { - return super.getIndexedReadMethod(); - } - }; - PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; - try { - runner.fillStatementWithBean(stmt, this, properties); - fail("Expected RuntimeException: tried to use a property with no no-arg read method"); - } catch (RuntimeException expected) {} - } - - public void testFillStatementWithBeanErrorReadMethodThrows() throws Exception { - PropertyDescriptor badReadMethod = new PropertyDescriptor("throwsException", getClass(), "getThrowsException", null); - PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; - try { - runner.fillStatementWithBean(stmt, this, properties); - fail("Expected RuntimeException: tried to call a method that throws"); - } catch (RuntimeException expected) {} - } - - public void testFillStatementWithBeanErrorReadMethodPrivate() throws Exception { - getPrivate(); - PropertyDescriptor badReadMethod = new BadPrivatePropertyDescriptor(); - PropertyDescriptor properties[] = new PropertyDescriptor[] { badReadMethod }; - try { - runner.fillStatementWithBean(stmt, this, properties); - fail("Expected RuntimeException: tried to call a private method"); - } catch (RuntimeException expected) {} - } - - class BadPrivatePropertyDescriptor extends PropertyDescriptor { - Method getPrivate; - BadPrivatePropertyDescriptor() throws Exception { - super("throwsException", QueryRunnerTest.class, "getThrowsException", null); - getPrivate = QueryRunnerTest.class.getDeclaredMethod("getPrivate", new Class[0]); - } - - @Override - public synchronized Method getReadMethod() { - if (getPrivate == null) return super.getReadMethod(); - return getPrivate; - } - } - - public void testRethrowNullMessage() { - // DBUTILS-40 - SQLException sqe = new SQLException((String)null); - QueryRunner qr = new QueryRunner(); - try { - qr.rethrow(sqe, "foo", new Object[] {"bar"}); - fail("rethrow didn't throw"); - } catch (SQLException expected) {} - } - - // indexed bean property - public String getIndexed(int index) { - return null; - } - - public String getThrowsException() { - throw new RuntimeException("this getter always throws an exception"); - } - - private String getPrivate() { - return null; - } - - private void assertArrayEquals(String message, Object[] expected, Object[] actual) { - assertEquals(message, Arrays.asList(expected).toString(), Arrays.asList(actual).toString()); - assertEquals(message, expected.length, actual.length); - } - - - private class NoOpFillStatement extends QueryRunner { - Object[] params; - @Override - public void fillStatement(PreparedStatement stmt, Object... params) - throws SQLException { - this.params = params; - } + private void callGoodUpdate() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + Integer ret = runner.update("update blah set ? = ?", "unit", "test"); + + verify(stmt, times(1)).executeUpdate(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(1)).close(); // make sure we closed the connection + + // call the other variation + when(meta.getParameterCount()).thenReturn(0); + ret = runner.update("update blah set unit = test"); + + verify(stmt, times(2)).executeUpdate(); + verify(stmt, times(2)).close(); // make sure we closed the statement + verify(conn, times(2)).close(); // make sure we closed the connection + + // call the other variation + when(meta.getParameterCount()).thenReturn(1); + ret = runner.update("update blah set unit = ?", "test"); + + verify(stmt, times(3)).executeUpdate(); + verify(stmt, times(3)).close(); // make sure we closed the statement + verify(conn, times(3)).close(); // make sure we closed the connection + } + + @Test + public void testGoodUpdate() throws Exception { + callGoodUpdate(); + } + + @Test + public void testGoodUpdatePmdTrue() throws Exception { + runner = new QueryRunner(true); + callGoodUpdate(conn); + } + + @Test + public void testGoodUpdateDefaultConstructor() throws Exception { + runner = new QueryRunner(); + callGoodUpdate(conn); + } + + // helper method for calling batch when an exception is expected + private void callUpdateWithException(Object... params) throws Exception { + Integer ret = null; + boolean caught = false; + + try { + when(meta.getParameterCount()).thenReturn(2); + ret = runner.update("select * from blah where ? = ?", params); + + verify(stmt, times(1)).executeUpdate(); + verify(stmt, times(1)).close(); // make sure we closed the statement + verify(conn, times(1)).close(); // make sure we closed the connection + } catch(SQLException e) { + caught = true; + } + + if(!caught) + fail("Exception never thrown, but expected"); + } + + @Test + public void testNoParamsUpdate() throws Exception { + callUpdateWithException(); } + @Test + public void testTooFewParamsUpdate() throws Exception { + callUpdateWithException("unit"); + } + + @Test + public void testTooManyParamsUpdate() throws Exception { + callUpdateWithException("unit", "test", "fail"); + } + + @Test(expected=SQLException.class) + public void testNullConnectionUpdate() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + when(dataSource.getConnection()).thenReturn(null); + + runner.update("select * from blah where ? = ?", "unit", "test"); + } + + @Test(expected=SQLException.class) + public void testNullSqlUpdate() throws Exception { + when(meta.getParameterCount()).thenReturn(2); + + runner.update(null); + } + + @Test + public void testExecuteUpdateException() throws Exception { + doThrow(new SQLException()).when(stmt).executeUpdate(); + + callUpdateWithException("unit", "test"); + } + + // + // Random tests + // + class MyBean { + private int a; + private double b; + private String c; + + public int getA() { return a; } + public void setA(int a) { this.a = a; } + public double getB() { return b; } + public void setB(double b) { this.b = b; } + public String getC() { return c; } + public void setC(String c) { this.c = c; } + } + + @Test + public void testFillStatementWithBean() throws Exception { + MyBean bean = new MyBean(); + when(meta.getParameterCount()).thenReturn(3); + runner.fillStatementWithBean(stmt, bean, new String[] { "a", "b", "c" }); + } + + @Test(expected=NullPointerException.class) + public void testFillStatementWithBeanNullNames() throws Exception { + MyBean bean = new MyBean(); + when(meta.getParameterCount()).thenReturn(3); + runner.fillStatementWithBean(stmt, bean, new String[] { "a", "b", null }); + } + + @Test(expected=SQLException.class) + public void testBadPrepareConnection() throws Exception { + runner = new QueryRunner(); + runner.update("update blah set unit = test"); + } }