Let's make all this a bit more concrete by looking at how you'd mock database access in a servlet.
Listing Five defines two classes. The JDBCSample class just shows you a typical JDBC
query. You get a connection from a DriverManager and use that connection to make a Statement.
You execute the query against the Statement, which returns a result set. The while loop
just looks at all the rows in the returned result set.
Now, let's say that we don't want to use the real database at all.
Looking at the earlier code, the DriverManager is a perfectly good Factory object as it stands,
so if we change the behavior of getConnection() to return our own mock connection, we can
control the entire access process.
Listing Five
package com.holub.tests;
import static org.junit.Assert.*;
import java.sql.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
class JDBCSample
{
public static void someMethod() throws Exception
{
// ...
Connection connection = null;
Statement statement = null;
try
{ connection = DriverManager.getConnection( "connectionURL", "username", "password" );
statement = connection.createStatement();
ResultSet result = statement.executeQuery( "select something from SomeTable where someColumn=fred" );
while( result.next() )
{ String someColumn = result.getString("ColumnName");
//...
System.out.println( someColumn );
}
}
finally
{
try{ if(statement != null) statement.close(); }catch(Exception e){}
try{ if(connection!= null) connection.close();}catch(Exception e){}
}
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest( {DriverManager.class, JDBCSample.class, JDBCTest.class} )
public class JDBCTest extends PowerMockito
{
@Test
public void doTest() throws Exception
{
// This answer is used for the result set's next() method. It simulates a one-row result set.
class MyAnswer implements Answer<Boolean>
{
private int timesCalled = 0;
@Override public Boolean answer(InvocationOnMock invocation) throws Throwable
{ return ++timesCalled <= 1;
}
}
ResultSet mockResultSet = mock( ResultSet.class );
when( mockResultSet.next() ).thenAnswer( new MyAnswer() );
when( mockResultSet.getString( Mockito.anyString() )).thenReturn("TestResult");
Statement mockStatement = mock( Statement.class );
when( mockStatement.executeQuery( Mockito.anyString() )).thenReturn( mockResultSet );
Connection mockConnection = mock( Connection.class );
when( mockConnection.createStatement() ).thenReturn( mockStatement );
mockStatic( DriverManager.class );
when(DriverManager.getConnection( Mockito.anyString(), Mockito.anyString(), Mockito.anyString()))
.thenReturn( mockConnection );
JDBCSample.someMethod();
}
}
The JDBCTest class at the bottom of Listing Five does exactly that.
To avoid forward references, the code is sort-of upside down, so let's look at it in reverse order.
The lines
mockStatic( DriverManager.class );
when(DriverManager.getConnection( Mockito.anyString(), Mockito.anyString(), Mockito.anyString()))
.thenReturn( mockConnection );
change the behavior of DriverManager.getConnection() to return a mockConnection rather than a real one.
The next line up:
Connection mockConnection = mock( Connection.class );
when( mockConnection.createStatement() ).thenReturn( mockStatement );
creates that mock connection. This particular mock returns a mockStatement when a statement is requested.
The preceding couple of lines do a similar thing with the result set. They override the behavior of
executeQuery() to return a mock result set:
ResultSet mockResultSet = mock( ResultSet.class );
when( mockResultSet.next() ).thenAnswer( new MyAnswer() );
when( mockResultSet.getString( Mockito.anyString() )).thenReturn("TestResult");
Statement mockStatement = mock( Statement.class );
when( mockStatement.executeQuery( Mockito.anyString() )).thenReturn( mockResultSet );
The only subtly is that the while loop that goes through the result set has to be modified
to simulate a one-row result set. PowerMock does that with the following "answer" object:
class MyAnswer implements Answer<Boolean>
{
private int timesCalled = 0;
@Override public Boolean answer(InvocationOnMock invocation) throws Throwable
{ return ++timesCalled <= 1;
}
}
The thenAnswer(...) call tells the mocking framework to call the answer object's answer(...) method
every time the associated method (next()) is called. This answer object returns true on the first call
and false on all subsequent calls.
Once all this infrastructure is in place, we can run the test by calling
JDBCSample.someMethod();
someMethod() will use all our mocks instead of the real objects, and the code will print
the string "TestResult" just as if it had looked up something in the database and fetched the value "TestResult" from the
actual database. Our test doesn't use a database at all, however.
Conclusion
This basic technique shows you how to use a mock framework to create objects both inside and outside the
donut. For the inside objects, use Factories to create objects rather than new, and then mock
the create() methods as necessary to produce your test objects rather than the real ones. I've used
mocks for the examples in this article, but you could also use a "spy" object to monitor
what happens to the donut-hole object. That is, your mock create() would call the real factory
to create objects, but then wrap that real object with a "spy" whose methods just call
identical methods in the wrapped real object. (A "spy" is a Gang-of-Four-type Decorator pattern that, for the most
part, just passes requests through to the decorated object.) The spy can record what happens to it,
however, so your test can transparently monitor how the contained object was used and then validate that
the contained object was used in the expected way.


