Friday, May 28, 2010

The power of a character.

I was faced with an issue this week.   Code was not working like it should, yet all of my unit tests passed.

The problem, I didn't have a unit test set up correctly.

Here's the scenario.  There are fields in the database that are used for tracking who the last person to update a record is and when that happened.  These fields do not get passed to the front end to prevent someone from using them.

When I save I cannot just insert/update the data from the front end I have to transfer that information into an object that includes the extra fields from the database.

But I only want to deal with that if the record has changed, so I use code that looks like this.

public ViewRecord save(ViewRecord viewRecord) {
  ViewRecord savedRecord = null;
  DBRecord existingRecord = db.getRecord(viewRecord.getId());
  if( recordChanged(viewRecord, existingRecord) ) {
    DBRecord saveRecord = buildRecord(viewRecord, existingRecord);
    DBRecord record = db.saveRecord(saveRecord);
    savedRecord = new ViewRecord(record);
  } else {
    savedRecord = viewRecord;  // did not save, but it didn't need to.
  }
  return savedRecord;
}

Basically every business object in my project does this exact same thing at save time.   The problem I ran into was with recordChanged();

It's code is this
private boolean recordChanged(ViewRecord viewRecord, DBRecord existingRecord) {
  if( existingRecord == null || !viewRecord.equals(new ViewRecord(existingRecord)) ) {
    return true;
  else
    return false;
}

Missing the ! on the equals means that a save will only happen if it is a new record or if nothing has changed.  Effectively turning the object into a write once object.

Code coverage tools may not catch this either.   There is nothing wrong with this code other than it doesn't do what I want.   I even had a unit test that had no changes and made sure that everything was being called to act as if it were saved.  

Unit tests are great, but be sure that you don't use copy and paste when setting them up.   Stop and think about each thing that you want to happen given a scenario, then make sure that you configure the mocks to expect only the calls that you want to have happen.  When you do that, the unit tests will help you catch quirks like a single missing !.

No comments: