Test Maintenance Cost

The change to the interface with Profile broke a number of tests in ProfileTest. We need to invest some effort to fix the tests, which highlights one of the costs of using unit tests in the first place; they need to be maintained regularly.

Refactoring

Refactoring is supposed to be an activity where we change the implementation of the code without changing its behavior.

Our tests are supposed to be a reflection of the program’s behavior. However, the reality is that we are changing the behavior of our classes in terms of how we expose that behavior through the classes’ interfaces.

We accept the cost of fixing broken tests because their value can be far greater than the effort we put into them.

There is certainly some benefit of being able to make changes without the worry of breaking other codes, especially since the cost of maintaining our tests isn’t always trivial. We truly recognize its expense when we encounter scenarios like the one we are in now, where we’ve broken a number of tests all at once.

As we move forward, think about the magnitude of failing tests as a negative design indicator. The more tests that break simultaneously, the more likely we have a design issue.

How to protect yourself?

We need to follow a few instructions to protect our code from failing miserably.

Problem with code duplication

Duplication of code is one of the most common design problems. From the stance of the tests themselves, duplication across tests creates two problems:

  1. It makes the tests harder to follow. If we expend three lines of code to create and populate an Answer object, that is three lines that a reader must step through and understand. Distilling them to a single concept, such as a helper method named createMatchingAnswer(), imparts immediate understanding to the reader.

  2. Extracting duplicate occurrences of small bits of code to a single method minimizes the impact when those small bits must change. Better to make a change in a single place than in numerous tests scattered across our source base.

Violation of the SRP

The need for several or even dozens of lines of code to set up unit tests is another indicator that we have problems with the design of our system. Violation of the SRP generally results in larger classes, which usually lead to more dependencies on other classes, which in turn demands more effort to set up our tests.

Find a way to split your larger classes!

Testing private methods

The compulsion to test private methods is another hint that our classes are too large.

More often than not, a spate of private methods suggests that the private behavior is better moved to a new class where it can become public behavior.

If unit testing seems hard, take the hint. Find ways to make unit testing easier by improving your design. This will decrease (but never eliminate) the cost of maintaining tests.

Unit test maintenance costs increase as our system’s design/code quality decreases.

Fixing our broken tests

The current tests in ProfileTest are mostly focused on managing what is now MatchSet objects. Extract these tests to the new MatchSetTest test class and make the changes necessary to get the test code compiled and passing. Specifically, we must pass a hash of question-text-to-Answer-object to create a MatchSet object. Add a utility method to simplify creating MatchSet objects and another to simplify adding Answer objects to a MatchSet.

Here’s what a couple of the tests look like in their new home:

Get hands-on with 1400+ tech skills courses.