Don't use non-test constants in unit tests
Tags: testing, java
Published: 2020-03-09 17:20:54

---
title: Don't use non-test constants in unit tests
published: true
description: Beware of reusing the same constants in unit tests! It's more dangerous than it is valuable.
tags: testing,java,
---

Every developer wants to promote code reuse. Generally, reuse is a good thing. It makes for more maintainable and more correct code.

There's one place where reuse shouldn't occur, though, and that's across the boundary between production and test code. Constants are a prime example of this. Let me show a concrete example to explain what I mean.

Let's assume you have this FizzBuzz code in your application in a `FizzBuzz.java` file:

```java
String[] generate(int start, int end) {
        String[] result = new String[end - start + 1];
        for (int x = 0, number = start; number <= end; x++, number++) {
            if (number % 15 == 0) {
                result[x] = "FizzBuzz";
            } else if (number % 3 == 0) {
                result[x] = "Fizz";
            } else if (number % 5 == 0) {
                result[x] = "Buzz";
            } else {
                result[x] = String.valueOf(number);
            }
        }
        return result;
    }
```

And you write a few basic unit tests like so, in a `FizzBuzzTest.java` file:
```java
@Test
    void fizzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(3, 3);
        assertEquals("Fizz", result[0]);
    }

    @Test
    void buzzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(5, 5);
        assertEquals("Buzz", result[0]);
    }

    @Test
    void fizzBuzzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(15, 15);
        assertEquals("FizzBuzz", result[0]);
    }
```

Some developers here are tempted to refactor the solution into constants in `FizzBuzz.java` which then also show up in the test:

```java
public class FizzBuzz {
    public static final String FIZZ = "Fizz";
    public static final String BUZZ = "Buzz";

    String[] generate(int start, int end) {
        String[] result = new String[end - start + 1];
        for (int x = 0, number = start; number <= end; x++, number++) {
            if (number % 15 == 0) {
                result[x] = FIZZ + BUZZ;
            } else if (number % 3 == 0) {
                result[x] = FIZZ;
            } else if (number % 5 == 0) {
                result[x] = BUZZ;
            } else {
                result[x] = String.valueOf(number);
            }
        }
        return result;
    }
```

And in `FizzBuzzTest.java`:

```java
@Test
    void fizzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(3, 3);
        assertEquals(FIZZ, result[0]);
    }

    @Test
    void buzzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(5, 5);
        assertEquals(BUZZ, result[0]);
    }

    @Test
    void fizzBuzzTest() {
        FizzBuzz fizzBuzz = new FizzBuzz();
        String[] result = fizzBuzz.generate(15, 15);
        assertEquals(FIZZ + BUZZ, result[0]);
    }
```

## Reasons not to do this

There are two key reasons why you shouldn't do this. 

### Tests change when production code changes

One reason not to do this is that it creates a situation where when production code is refactored, tests will unnecessarily get refactored alongside. Consider what would happen if you did an IDE refactoring to rename the constant `FIZZ` or move it to another class. You would also see the refactoring automatically touch the test code. That can be disconcerting because it introduces doubt as to whether or not the test is still accurate.

The ideal test code is code that doesn't change when production code is refactored, and the tests keep passing. This tells you your refactor is 100% safe. You should not usually have to change test code to change production code, and if you do, it should be because functionality _actually is_ changing. Unfortunately, with tests that reference constants from production, this guarantee goes away because they are coupled together.


### Tests will pass when the code is wrong

More importantly, tests that reference production constants can cause a situation where the code is actually wrong but the unit test passes anyway.

Consider the case where there's a misspelling in "Fizz" or even the wrong value, as here where it has the value `"Buzz"`:

```java
public class FizzBuzz {
    public static final String FIZZ = "Buzz";

    // . . .
```

The unit test will still pass, because the test is referencing against the same wrong `FizzBuzz.FIZZ` variable that the production code is referencing.

That's really scary and downright lethal in actual production code where the value of a constant often matters to some external interaction that the system makes. For example, a query parameter on a web service call or the name of a form field received from the front end. 

Unit testing should always be the software equivalent to [double-entry bookkeeping](https://www.dummies.com/business/accounting/the-basics-of-double-entry-bookkeeping/). This means that unit tests are meant to repeat values as a way to provide a second point-of-view on the correctness of a given piece of code. The unit test should check for the literal String value `"Fizz"` even if the production code uses a constant. This guarantees that the literal value expected from the constant is maintained.

## Conclusion

For these reasons, I have concluded that test code should not reference constants in production code. Hope it helps.