Test legacy JDBC code without the database

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 388 - 391)

Problem

You have inherited legacy JDBC code and would like to test it without dragging a database along with you.

Background

Your big problem—and it’s not your fault, but it is your problem—is that you can- not apply the refactorings that we have described in this chapter to the JDBC code you need to test. We feel bad for you, but rather than just feel bad, we can help.19 This recipe describes how to use Mock Objects (www.mockobjects.com) to test those JDBC calls without a database.

Recipe

Before we begin, we would like to refer you to the Mock Objects article “Develop- ing JDBC Applications Test First” (www.mockobjects.com/wiki/DevelopingJdbc ApplicationsTestFirst). Even if you are not writing your application test-first, the article provides good examples on the various parts of the Mock JDBC API that Mock Objects provides. We have no desire to repeat good documentation in print, where it may become stale. Instead, we will show you one example of testing the JDBC implementation of our CatalogStore using the Mock JDBCAPI. After all, you may not have the Web in front of you just now.

Let us reprise the example we used in recipe 10.2, “Verify your SQL commands.”

This time, rather than verifying just the SQL string, we will add assertions pertain- ing to using the JDBCAPI correctly. We will also assume that the JDBC code we want to test is not subject to change, being legacy code that management is deathly afraid to touch.20 We submit a mock data source to the JDBC implementation of our CatalogStore. This mock data source is primed with a mock connection and a mock prepared statement, and despite all these mocks—which might eventually make you wonder what exactly you are testing—the point is to verify that the JDBC implementation of the CatalogStore knows how to talk to the classes in the JDBC API. Listing 10.16 shows the test for adding a coffee bean product to the catalog.

19That same colleague, in his distinctive accent, liked to say, “All you can do is cry.” In this case, you can do more.

20We hope that, as you continue reading this book, you realize—and perhaps are able to convince your man- agement of the fact—that when you have tests as a safety net, change is not painful, but rather beneficial.

358 CHAPTER 10 Testing and JDBC

public void testAddProduct() { Product toAdd =

new Product("999", "Colombiano", Money.dollars(9, 0));

MockDataSource dataSource = new MockDataSource();

MockConnection2 connection = new MockConnection2();

connection.setExpectedCloseCalls(1);

final MockPreparedStatement addProductStatement = new MockPreparedStatement();

addProductStatement.setExpectedClearParametersCalls(1);

addProductStatement.addExpectedSetParameters(

new Object[] { "999", "Colombiano", new Integer(900)});

addProductStatement.addUpdateCount(1);

addProductStatement.setExpectedCloseCalls(1);

dataSource.setupConnection(connection);

connection.setupAddPreparedStatement(

"insert into catalog.beans "

+ "(productId, coffeeName, unitPrice) values "

+ "(?, ?, ?)", addProductStatement);

CatalogStore store = new CatalogStoreJdbcImpl(dataSource);

store.addProduct(toAdd);

addProductStatement.verify();

connection.verify();

dataSource.verify();

}

The majority of this test is setup work, which is common for mock objects-based tests. We create a mock data source, mock connection, and mock prepared state- ment. We tell the prepared statement to expect to be used in the following fashion:

1 The CatalogStore will invoke clearParameters() once.

2 The CatalogStore will set the parameters that correspond to the Product object we want to add to the catalog. Notice the mapping of the unit price property from a Money object to the equivalent amount of money in cents.

3 The CatalogStore will update one row, represented by the property Mock- PreparedStatement.updateCount.

4 The CatalogStore will close the statement once.

Listing 10.16 A database test using the Mock Objects JDBC API

359 Test legacy JDBC code

without the database

Similarly, we tell the connection how to expect to be used, and even the data source itself. After we perform the operation—addProduct() in this case—that uses these JDBC objects, we ask them to verify themselves and complain if their expectations are not met. All this without involving a real database.

Discussion

The one thing we do not like about these tests is that although the database is not involved, the tests themselves remain brittle: each test depends on both the cor- rectness of your JDBC client code and its ability to map the data correctly. Code that tries to serve two masters is easily distracted.21 We prefer to test different behaviors separately, but we understand that with true legacy code—code without tests that you cannot change—you have no choice. Using mock objects provides a coping mechanism for the problem, but if you have the opportunity, you ought not to stop here.

We recommend that you take the JDBC client code and, if the design makes this feasible, extract business-oriented interfaces from them. That is, extract interfaces whose methods and parameters and return types only express domain concepts. For example, if you have a data access object that finds all customers whose accounts are 30 days past due, then extract the interface CustomerStore with method find- PastDue(int days) and place your legacy JDBC client code inside an implementa- tion of CustomerStore, perhaps called CustomerStoreLegacyImpl.22 You can then treat your legacy implementation of the “Store” interfaces as a giant black box.

Over time you can replace parts of it with implementations you can actually test!

Moreover, you can do that at your leisure. No hurry. It may take a long time to refactor completely away from the legacy code, but at least you know that you can do it—that it is only a matter of time.

Related

■ 10.2—Verify your SQL commands

■ Mock Objects project (www.mockobjects.com)

■ “Developing JDBC Applications Test First”

(www.mockobjects.com/wiki/DevelopingJdbcApplicationsTestFirst)

21A liberal paraphrase of Irving Chernev, the great chess writer, in Logical Chess: Move by Move.

22“Impl” is one of those rare times when we do not mind using an abbreviation. The exception proves the rule.

360 CHAPTER 10 Testing and JDBC

Một phần của tài liệu Manning JUnit recipes practical methods for program (Trang 388 - 391)

Tải bản đầy đủ (PDF)

(753 trang)