Avoid using production constants in your tests

Consider the following piece of code:

class Constants {

    companion object {
        const val HELLO_WORLD = "Hello, world!"
    }
    
    fun helloWorld(): String {
        return HELLO_WORLD
    }
}

I often see such a class tested as follows:

@Test
fun test_hello_world() {
    val result = Constants().helloWorld()
    assertThat(result).isEqualTo(HELLO_WORLD)
}

So, the test re-uses the public production constant HELLO_WORLD.

Why is that a problem?

When the value of the constant changes unintendedly, for example through refactoring or search/replace, the test won’t fail.

This is an example of a weak test: it fails to catch any changed behaviour in the production code.

The mutation test tool https://pitest.org/ won’t catch it either, since it often cannot mutate inline constants because of compiler optimizations: https://pitest.org/quickstart/mutators/#INLINE_CONSTS

Solution

Refactor so you use the literal value in the test.

@Test
fun test_hello_world() {
    val result = Constants().helloWorld()
    assertThat(result).isEqualTo("Hello, world!")
}

This way, when refactoring changes the constant’s value unintendedly, the test will catch it.

But it’s duplicate code!

It’s not. The value in the test class serves an entirely different purpose than in the production code, namely it’s used for testing a value, not using it.

To summarize

Never use your production constants directly in your test classes!

Leave a Reply

Your email address will not be published. Required fields are marked *