Рубрики
Без рубрики

Не используйте константы, не являющиеся тестовыми, в модульных тестах

Остерегайтесь повторного использования одних и тех же констант в модульных тестах! Это более опасно, чем ценно. С тегом “тестирование”, java.

Каждый разработчик хочет способствовать повторному использованию кода. Как правило, повторное использование – это хорошо. Это делает код более удобным для обслуживания и более корректным.

Однако есть одно место, где повторное использование не должно происходить, и оно находится на границе между производственным и тестовым кодом. Константы являются ярким примером этого. Позвольте мне показать конкретный пример, чтобы объяснить, что я имею в виду.

Давайте предположим, что у вас есть этот код FizzBuzz в вашем приложении в FizzBuzz.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;
    }

И вы пишете несколько базовых модульных тестов вот так, в FizzBuzzTest.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]);
    }

Некоторые разработчики здесь испытывают соблазн преобразовать решение в константы в FizzBuzz.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;
    }

И в FizzBuzzTest.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]);
    }

Причины не делать этого

Есть две основные причины, по которым вам не следует этого делать.

Тесты меняются при изменении производственного кода

Одна из причин, по которой этого не следует делать, заключается в том, что это создает ситуацию, когда при рефакторинге производственного кода тесты будут без необходимости подвергаться рефакторингу. Подумайте, что произойдет, если вы выполните рефакторинг IDE для переименования постоянная шипение или переместите его в другой класс. Вы также увидите, что рефакторинг автоматически коснется тестового кода. Это может привести в замешательство, поскольку вызывает сомнения в том, является ли тест все еще точным.

Идеальный тестовый код – это код, который не меняется при рефакторинге производственного кода, и тесты продолжают проходить. Это говорит вам, что ваш реактор на 100 % безопасен. Обычно вам не нужно менять тестовый код, чтобы изменить производственный код, и если вы это сделаете, это должно быть потому что функциональность на самом деле меняется. К сожалению, с тестами, которые ссылаются на константы из производства, эта гарантия исчезает, потому что они связаны друг с другом.

Тесты будут проходить, когда код неверен

Что еще более важно, тесты, которые ссылаются на производственные константы, могут привести к ситуации, когда код на самом деле неверен, но модульный тест все равно проходит.

Рассмотрим случай, когда в слове “Шипение” есть ошибка или даже неправильное значение, как здесь, где оно имеет значение "Гудение" :

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

    // . . .

Модульный тест все равно пройдет, потому что тест ссылается на тот же неправильный FizzBuzz. FIZZ переменная, на которую ссылается производственный код.

Это действительно страшно и совершенно смертельно в реальном производственном коде, где значение константы часто имеет значение для некоторого внешнего взаимодействия, которое производит система. Например, параметр запроса при вызове веб-службы или имя поля формы, полученного от внешнего интерфейса.

Модульное тестирование всегда должно быть программным эквивалентом двойной бухгалтерии . Это означает, что модульные тесты предназначены для повторения значений, чтобы обеспечить вторую точку зрения на правильность данного фрагмента кода. Модульный тест должен проверять буквальное строковое значение "Шипение" даже если производственный код использует константу. Это гарантирует, что буквальное значение, ожидаемое от константы, будет сохранено.

Вывод

По этим причинам я пришел к выводу, что тестовый код не должен ссылаться на константы в производственном коде. Надеюсь, это поможет.

Оригинал: “https://dev.to/scottshipp/don-t-use-non-test-constants-in-unit-tests-3ej0”