1.2 Desarrollo teórico instrumental sobre las posibilidades configurativas de nuevas identidades masculinas desde la perspectiva de género
1.2.3. Actualidades: trascender el género normativo
myTableChangedCalls.verify( );
myTableChangedParameter0Values.verify( );
}
}
The generated code relies on code found in the Mock Objects framework for keeping track of expectations, such as the expected events or number of times a method was called. You use this class almost exactly like you would use the hand-coded mock object, as shown in Example 6-5 (although the method names are slightly different).
Here is how you can run MockMaker from an Ant buildfile:
<path id="classpath.mockmaker">
<pathelement path="${dir.build}"/>
<pathelement
location="${env.MOCKMAKER_HOME}/mockmaker.jar"/>
<pathelement
location="${env.MOCKMAKER_HOME}/mmmockobjects.jar"/>
<pathelement location="${env.MOCKMAKER_HOME}"/>
</path>
...
<target name="generateMockObjects" depends="prepare">
<java fork="true" classname="mockmaker.MockMaker"
output="${dir.generatedSrc}/MockTableModelListener.java">
<classpath refid="classpath.mockmaker"/>
<arg line="javax.swing.event.TableModelListener"/>
</java>
</target>
6.6.4 See Also
Recipe 6.2 and Recipe 6.3 show how to hand-code mock objects that look similar to the code generated by MockMaker. The Mock Objects web site, http://www.mockobjects.com, lists URLs for several other mock object generation tools, including Easy Mock, Mock Creator, and Mock Doclet.
6.7 Breaking Up Methods to Avoid Mock Objects
6.7.1 Problem
You want to test a method without resorting to the complexity of mock objects.
6.7.2 Solution
Split the method into smaller pieces, ensuring that each piece performs one task. Small, single-purpose methods improve code quality in addition to making them testable.
6.7.3 Discussion
Example 6-12 shows a method that is hard to test. It is hard because you must create a mock
ResultSet
implementation in order to write your tests.Example 6-12. Hard to test
// fetch an account type code from the database and convert it
// into one of the Account constants
int getAccountType(ResultSet rs, String acctTypeColName)
throws SQLException, DataSourceException {
String acctStr = rs.getString(acctTypeColName);
if ("SA".equals(acctStr)) {
return Account.SAVINGS;
}
if ("CH".equals(acctStr)) {
return Account.CHECKING;
}
throw new DataSourceException("Unknown account type: " +
acctStr);
}
The fundamental problem is that this method performs two tasks, rather than one. It is also a little messy because it throws two types of exceptions. The first task is to retrieve data from the
ResultSet
. The second task is to convert that data into some other form.When confronted with a method like this, do not try to write a sophisticated unit test. Instead, first try to simplify the method. Example 6-13 shows a simplified version of this method. It is now assumed that the caller obtains the account code from the database before calling this method, whose sole purpose is converting that string into a Java constant.
// convert a database account code, such as "CH", into a Java
constant
int getAccountType(String acctTypeStr)
throws DataSourceException {
if ("SA".equals(acctTypeStr)) {
return Account.SAVINGS;
}
if ("CH".equals(acctTypeStr)) {
return Account.CHECKING;
}
throw new DataSourceException("Unknown account type: " +
acctTypeStr);
}
You can now test this method without resorting to mock objects. We also eliminated the extra
SQLException
because we no longer use JDBC in this method. Example 6-14 shows the test.Example 6-14. Test for the getAccountType( ) method
public void testGetAccountType( ) throws Exception {
AccountFactory acctFact = new AccountFactory( );
assertEquals("account type", Account.CHECKING,
acctFact.getAccountType("CH"));
assertEquals("account type", Account.SAVINGS,
acctFact.getAccountType("SA"));
try {
acctFact.getAccountType("bogus");
fail("Expected DataSourceException");
} catch (DataSourceException expected) {
}
}
6.7.4 See Also
This method was taken from Example 6-8 earlier in this chapter.
6.8 Testing Server-Side Business Logic
6.8.1 Problem
You want to test business logic that normally depends on a database, but mocking out the low-level SQL is far too complex.
6.8.2 Solution
Organize your server-side code using business objects and database access objects (DAOs). Place all business logic in your business objects, and all database access in your DAOs. Use a factory to create mock implementations of your DAOs when testing your business objects.
6.8.3 Discussion
We showed how to write mock objects to simulate low-level SQL code earlier in this chapter. It is a useful technique for testing the data access tier of your application, but tends to be far too complex for business logic tests. For business objects, you should strive to create mock implementations of the entire data access tier, rather than mock implementations of the JDBC interfaces.
Figure 6-1 illustrates a common design pattern for server-side Java code. In this diagram, either an EJB or a servlet dispatches method calls to
CustomerBO
, a business object that contains server- side business logic. The business object is what we would like to test.Figure 6-1. Business object and DAO pattern
The first box in Figure 6-1 shows either an EJB or a servlet. This pattern works well with either approach, although the EJB approach allows you to easily invoke many different business objects under the umbrella of a single transaction. Regarding testing, the business object pattern is fantastic because you can test
CustomerBO
as you would test any other standalone Java class. That is, you don't need to run your tests inside of the application server.The second key to making business objects testable is keeping data access code separate. The
CustomerDAO
interface defines an API to a data source, and theOracleCustomerDAO
is an Oracle-specific implementation. When using this approach, your business objects generally locate the correct DAO implementations using some sort of factory object. Example 6-15 shows what some of the methods inCustomerDAO
might look like.Example 6-15. CustomerDAO methods