|
4.5. Running a Test with EasyMockIt's time to run a test case. Since you're testing a JDBC application, it makes sense for us to verify that it's been used correctly. Let's say that you wanted to test a single turn signal, one time. One way would be to stand behind the car, and then have someone inside activate the signal. If it didn't blink, then you'd say so. But say that you wanted to test the device before you put it into a car. One strategy would be to plug in a volt meter, a device that measures electricity, instead of a light bulb. Then, if the signal did not generate the right amount of electricity at the right time, the test would fail. That's how a mock object works. Sometimes, instead of simulating the real world (like our stub in Chapter 1 that simulated a database), you want to know how your object under test is using its interfaces. You might use a mock object instead of a JDBC interface to make sure that the application opens the connection and closes it, just as you expect. 4.5.1. How do I do that?You'll first need to install EasyMock. Download the latest version from http://www.easymock.org and place the easymock.jar file in your project's classpath. We've added it to our /lib folder. Next, you can establish the collection of mock objects you'll need (Example 4-13). You are effectively drilling down through the JDBC interfaces, and it turns out you will use four of them. Example 4-13. ControllerTest.javapublic void testGetBikesWithMocks( ) throws Exception { DataSource mockDataSource; Connection mockConnection; Statement mockStatement; ResultSet mockRS; MockControl controlDataSource = MockControl.createControl(DataSource.class); MockControl controlConnection = MockControl.createNiceControl(Connection.class); MockControl controlStatement = MockControl.createControl(Statement.class); MockControl controlRS = MockControl.createControl(ResultSet.class); mockDataSource = (DataSource)controlDataSource.getMock( ); mockConnection = (Connection)controlConnection.getMock( ); mockStatement = (Statement)controlStatement.getMock( ); mockRS = (ResultSet)controlRS.getMock( ); Next, you will set the expectations. Using EasyMock, you do this by recording a working version of your intended test case. When you do the record, you're telling EasyMock how the application should behave (Example 4-14). Example 4-14. ControllerTest.javamockDataSource.getConnection( ); controlDataSource.setReturnValue(mockConnection); mockConnection.createStatement( ); controlConnection.setReturnValue(mockStatement); mockStatement.executeQuery("SELECT * FROM bikes"); controlStatement.setReturnValue(mockRS); controlRS.expectAndReturn(mockRS.next( ), false); controlStatement.expectAndReturn(mockStatement.getWarnings( ), null); mockRS.close( ); mockStatement.close( ); mockConnection.close( ); Next, you'll play the test case back, as in Example 4-15. Example 4-15. ControllerTest.javacontrolConnection.replay( ); controlDataSource.replay( ); controlStatement.replay( ); controlRS.replay( ); Finally, you will kick off the actual test and verify the test case (Example 4-16). If the verification step fails, then the test case will fail, just as if an assertion failed in basic JUnit. Example 4-16. ControllerTest.javaJDBCRentABike jstore = (JDBCRentABike)store; jstore.setDataSource(mockDataSource); List bikes = store.getBikes( ); controlConnection.verify( ); controlDataSource.verify( ); controlStatement.verify( ); controlRS.verify( ); Let's say that you forgot to record the ResultSet being closed (by leaving out the call mockRS.close( )). Example 4-17 shows the results of running the unit test. Example 4-17. Output from running ControllerTest.javajunit.framework.AssertionFailedError: Unexpected method call close( ): close( ): expected: 0, actual: 1 at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:44) at $Proxy3.close(Unknown Source) at org.springframework.jdbc.support.JdbcUtils.closeResultSet(JdbcUtils.java:69) at org.springframework.jdbc.core.JdbcTemplate$1QueryStatementCallback.doInStatement (JdbcTemplate.java:259) at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:204) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:266) at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:270) at com.springbook.JDBCRentABike.getBikes(JDBCRentABike.java:56) at JDBCFacadeTest.testGetBikesWithMocks(JDBCFacadeTest.java:75) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at com.intellij.rt.execution.junit2.JUnitStarter.main(Unknown Source)
4.5.2. What just happened?You've seen dynamic mock objects in action. The nice thing about dynamic mock objects is that you can test sophisticated user interfaces, like JDBC, without having to simulate all of its behavior. 4.5.3. What about......other mock object frameworks? You don't have to use EasyMock. Other mock object frameworks work as well. The overall flow is the same. For any framework, we'll see the following steps (similar to the ones described in the sidebar above):
For some, mock objects might seem to be awkward. Stay with them, and you'll learn to appreciate how they ease your testing burden. Just don't throw away everything else in your toolbox to make room for this, or any other, golden hammer. You have just seen how to use simple JDBC with Spring. In the next chapter, you'll see how Spring can do many of the same things for other persistence solutions, including full object relational frameworks. |
|