Passing Contract Tests While Refactoring Them

In my last blog post I explained how I at one time created a new implementation to pass the same contract tests as another implementation, but due to having to refactor the tests at the same time (the two implementations have a different concurrency model, so the contract tests must be decoupled from it), I missed a problem (wrote some dead code). Since then I've retried that refactoring/implementation using a different approach, as I explained in the comments of that blog post.

One option would have been to refactor the whole contract test class before starting to implement it, but that goes against my principles of doing changes in small steps and having fast feedback loops. So the approach I tried is as follows:

  1. Extract a contract test from the old implementation's tests by extracting factory methods for the SUT, creating the abstract base test class and moving the old implementation's test methods there.
  2. Create a second concrete test class which extends the contract tests, but override all test methods from the contract test and mark them ignored. This avoids getting lots of failing tests appearing at once. Maybe mark each of the overridden versions with a "TODO: refactor tests" so as to not forget the next step.
  3. Inspect the contract test method which you plan to implement next and see if it requires some refactoring before it would work for both implementations. Refactor the test if necessary. This gives a systematic way for updating the contract tests in small tests and avoids refactoring while tests are red.
  4. Unignore one contract test method and implement the feature in the new implementation. This lets you focus on passing just one test at a time, as is normal in TDD.

I recorded my experiment of using this strategy as a screencast:

Download as MP4

More Let's Code Screencasts

No comments:

Post a Comment