The Testing Anti-Patterns Drafts (draft 3) : Testing a class with itself

Among other interesting engineering marvels, cars tend to have an accelerator and a speed meter. It would obviously make sense to test both in order to verify whether they’re doing their job properly.

If a test consists of putting the pedal to the metal and watching the speed meter react, would that give us confidence that the accelerator works as intended? To an extend, it would, especially if the speed meter has had its own fair share of testing. However, if a couple of days down the line somebody worked on the speed meter to give it a stylish racing white panel and accidentally broke the way the meter displays speed, the accelerator test would erroneously indicate that the accelerator is broken.

This kind of danger is apparent when testing a class’s methods with other methods of the same class:

class Repository
  def save record
    OrmFramework.save record
  end

  def load id
    OrmFramework.load id
  end
end

class RepositoryTest < Test::Unit::TestCase
  def test_should_save_record
    record = Record.new
    repository = Repository.new
    repository.save record
    assert_equal record, Repository.load record.id
  end
end

If we changed the implementation of the load method, the test_should_save_record test would potentially break, although the save method still works as intended. A more sane test would look like:

def test_should_save_record
  record = Record.new
  Repository.new.save record
  assert_equal record, OrmFramework.load record.id
end

The important detail is that OrmFramework is an external import, whose implementation we know is not going to change and we can assume it has been tested to work as expected. The moral is to avoid testing code with code that has not been frozen and is still code in progress.

If you'd like to follow the discussion, feel free to grab The Testing Anti-Patterns Drafts feed.

Comments are closed.