Unit Testing and DRY Principle – Best Practices

dryunit testing

I have a recurring question when writing unit tests for code that involves constant string values.

Let's take an example of a method/function that does some processing and returns a string containing a pre-defined constant. In python, that would be something like:

STRING_TEMPLATE = "/some/constant/string/with/%s/that/needs/interpolation/"

def process(some_param):
    # We do some meaningful work that gives us a value
    result = _some_meaningful_action()
    return STRING_TEMPLATE % result

If I want to unit test process, one of my tests will check the return value. This is where I wonder what the best solution is.

In my unit test, I can:

  1. apply DRY and use the already defined constant
  2. repeat myself and rewrite the entire string
def test_foo_should_return_correct_url():
    string_result = process()

    # Applying DRY and using the already defined constant
    assert STRING_TEMPLATE % "1234" == string_result

    # Repeating myself, repeating myself
    assert "/some/constant/string/with/1234/that/needs/interpolation/" == url

The advantage I see in the former is that my test will break if I put the wrong string value in my constant. The inconvenient is that I may be rewriting the same string over and over again across different unit tests.

Best Answer

There's a little warning about the DRY principle that many people aren't aware of: Don't get so obsessed about avoiding repetition that you remove 'duplicates' of things that appear to be the same but are actually conceptually very different.

As an extreme example you wouldn't try to remove the repetition of the duplicates of '/s', 'th', 'on', 'nt', etc within your original string.

In your particular example, you don't/shouldn't care about implementation details of your code under test. (I.e. that it uses the STRING_TEMPLATE const.) Your test cares about the fact that when you:

void Test() {
  DoSetup(...);
  ActualResult = DoProcess(...);
  Check(ActualResult == "/some/constant/string/with/1234/that/needs/interpolation/");
}

The fact that "/some/constant/string/with/1234/that/needs/interpolation/" is similar to STRING_TEMPLATE should be considered coincidental, not a violation of DRY.

NOTE: This of course doesn't apply if you're actually testing some localisation aspect of your system, or if your system is affected by localisation that you initially set up via STRING_TEMPLATE. Because then the duplication is explicit.

Related Topic