7

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.

4 Answers 4

11

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.

2
  • I like your answer. I agree that the fact that we use a constant is an implementation detail and if you treat it as such, you should ignore it in your unit test. However, given that you are 'omniscient' about the code when you write unit tests, sometimes it's not obvious to see what is only an implementation detail and what matters.
    – Rodrigue
    Jul 2, 2012 at 6:54
  • 2
    Also note that your tests often act as a requirement specification document. In this case if you use the constant in tests and refactor, your change applies to both code and tests and tests still pass even though you modified the app's behavior and requirements may not be met any more. If you have "duplicated" values, and your test says you should return value "A", that's a specification. When you modify code so that it returns "B", your test will fail and you will either realize that you should not change that value, or consciously modify the test, saying that requirements are different now. Jul 14, 2015 at 12:00
2

First off in your example, you are actually testing two things in a single unit test, a unit test should only test one thing at a time.

What you should be testing:

  1. the constant value is in the format or a proper URL with a single % wildcard position.
  2. the string replacement inside the build_url function actually produces a valid URL given a valid input to the function

in addition you should test

  1. the constant fails to validate the constant value if it is not a valid URL pattern with a % position ( and all the mutations of this )
  2. the string replacement inside function fails to create valid URLs with invalid input

These should be two separate unit tests.

1
  • I like the idea of testing the constant separately. That is what I often do: I write a test that interpolates my template and makes sure I get the expected output. After that, I use the constant in my other unit tests.
    – Rodrigue
    Jul 1, 2012 at 17:42
1

I usually prefer to manually specify the expected result. While it's unlikely that the string interpolation in your example will break, there might be a case where you use a third party library to build the result. Using manual results will uncover version incompatibilities in these libraries. Also, there's always the danger of strange environmental circumstances that might break the function, like locale, encoding, 32- / 64-bit platforms, or similar. Sometimes, those actually break the function. Other times, they just broke the unit tests with their manually typed results. In either case, it's worthwhile to check.

If you test functions without side effects, you could use data-driven testing, which allows you to keep test inputs and expected outputs close together.

However, in your particular example, it might be a good idea to not hard-code the URL_TEMPLATE, but read it from some configuration file. This will ease the transition between different environments.

1
  • Thanks for the answer. I think I made my example too simple though. I don't really want to make sure the interpolation works. I am more thinking of a case where foo would do more things and as part of generating its output, use a constant I have defined. I would then need to check that somewhere, the returned value contains the right string. I will figure a better example.
    – Rodrigue
    Jul 1, 2012 at 17:34
-2

This is a good candidate for "Things that shouldn't be unit tested."

What business logic are you testing?

That the constant is set correctly? The test is just repeating the code then (which is a sign you shouldn't be testing it).

That the language is doing string replacement properly? It's not your job to test the language.

4
  • 1
    My example is simple to stupidity which probably doesn't help my point. I'm thinking of the case where my foo would do other things and at some stage use the string constant to produce it's output. How would I check that the output contains the value of the constant?
    – Rodrigue
    Jul 1, 2012 at 16:42
  • @rodrigue: what's the requirement? if the requirement refers to the constant (regardless of value) then test versus it. If the requirement refers to the value of the constant (and the constant is an implementation detail) then test the value.
    – Telastyn
    Jul 1, 2012 at 16:44
  • Downvote for "candidate for things that shouldn't be tested" - since the question isn't about the example
    – Murph
    Jul 1, 2012 at 18:30
  • @murph: regardless, the constant string isn't part of what you should be testing.
    – Telastyn
    Jul 1, 2012 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.