Jeff's Blog

Musings about software development, Java, OO, agile, life, whatever.


Friday, November 04, 2005 
Database TDD Part 17: Mocking Access

I have a failing test due to database dependencies:

package application;

import junit.framework.*;

public class ApplicationTest extends TestCase {
   public void testVerifyUser() {
      final String name = "name";
      final String password = "password";
      Application application = new Application();
      assertFalse(application.isRegistered(name, password));
      application.registerUser(name, password);
      assertTrue(application.isRegistered(name, password));
   }
}
I want to use an implementation of the UserAccess class that exists solely for controlling tests against Application. In the test, I introduce a setter method call to drop in a mock UserAccess implementation (to be defined):
   public void testVerifyUser() {
      final String name = "name";
      final String password = "password";
      Application application = new Application();
      application.setUserAccess(new MockUserAccess());
      assertFalse(application.isRegistered(name, password));
      application.registerUser(name, password);
      assertTrue(application.isRegistered(name, password));
   }
The Application class must change to accommodate this test intrusion:
package application;

import domain.*;

public class Application {
   private UserAccess userAccess = new UserAccess();

   public void registerUser(String name, String password) {
      User user = new User(name, password);
      userAccess.save(user);
   }

   public boolean isRegistered(String name, String password) {
      User user = userAccess.find(name);
      return user != null;
   }

   void setUserAccess(UserAccess userAccess) {
      this.userAccess = userAccess;
   }
}
I'd consider annotating the setUserAccess method with a comment saying it's designed for testing use--but only if someone mentioned it.

The mock class is really dumb at this point. Its needs are minimal based on the existing content of ApplicationTests. The class will need to change as I add more comprehensive tests against Application.

package application;

import persistence.*;
import domain.*;

public class MockUserAccess extends UserAccess {
   private static User user;
   public User find(String key) {
      return MockUserAccess.user;
   }

   public void save(Persistable persistable) {
      MockUserAccess.user = (User)persistable;
   }
}
Subclass-based mocks can introduce some interesting issues. You do have to have a clear understanding of the behavior of the superclass, otherwise you run the risk of side effects whacking your tests. It's generally safer to not go with this approach. Here, the benefit is that I don't have to implement the unused PersistableMetadata methods.

Is this mocking tactic scaleable? What about when the Application class has to work with half a dozen persistence APIs? Do we want half a dozen setter methods used solely for the test? It's too early for me to tell or even care.

The DataAccess subclasses, UserAccess and CustomerAccess, are still being tested using live JDBC calls. My take: that's a good thing. It's too easy for code to get out of sync with the database under test. If you have a few dozen DataAccess subclasses, perhaps they fall into the category of "integration' tests. I'm not compelled to give them a Meaningful Name like that. But if they get in the way--if they start bogging things down in terms of execution time--I'll foist them into a test suite that gets run just a bit less frequently. And, at that time, come up with a trustworthy mechanism to demonstrate that DataAccess subclasses and the Persister class are correctly coordinating with each other.

For now, I'm comfortable knowing that I could begin to build the rest of the application class in advance of any persistence details.

code


Comments: Post a Comment

Links to this post:

Create a Link



<< Home

RSS Feed (XML)

Archives

February 2004   March 2004   May 2004   September 2004   October 2004   January 2005   February 2005   September 2005   October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   June 2006   August 2006   January 2007   February 2007   March 2007   April 2007   September 2007   October 2007   November 2007   December 2007   January 2008  

This page is powered by Blogger. Isn't yours?