Test Methods Must Share Nothing

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

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

Модульные тесты, естественно, дублируют много кода. Тестовые методы содержат похожий или почти идентичный функционал, и это почти неизбежно. Хорошо, мы можем использовать больше функций @Before и @BeforeClass, но иногда это просто невозможно. У нас может быть, скажем, 20 тестовых методов в одном файле FooTest.java. Подготовить все объекты в одном “before” невозможно. Поэтому мы должны делать определенные вещи снова и снова в наших тестовых методах.

Давайте посмотрим на один из классов в нашем Takes Framework: VerboseListTest. Это модульный тест, и у него есть проблема, о которой я пытаюсь вам рассказать. Посмотрите на эту приватную литералу MSG. Она используется в первый раз в методе setUp() в качестве аргумента конструктора объекта, а затем в нескольких тестовых методах, чтобы проверить, как этот объект ведет себя. Позвольте упростить этот код:

Вот что происходит в VerboseListTest и это очень неправильно. Почему? Потому что это общая литера MSG создает ненатуральную связь между этими двумя методами тестирования. У них ничего общего, потому что они проверяют разные поведения класса Foo. Но эта приватная константа связывает их вместе. Теперь они как-то связаны друг с другом.

Если и когда мне нужно изменить один из методов тестирования, мне может понадобиться изменить и другой. Скажем, я хочу посмотреть, как ведет себя doSomethingElse(), если инкапсулированное сообщение является пустой строкой. Что я делаю? Я изменяю значение константы FooTest.MSG, которая используется другим методом тестирования. Это называется связью. И это плохо.

Что мы делаем? Что ж, мы можем использовать строковую литеру "something" в обоих методах тестирования:

Как видите, я избавился от метода setUp() и от приватной статической литералы MSG. Что у нас теперь? Дублирование кода. Строка "something" появляется четыре раза в классе теста. Ни один статический анализатор не потерпит этого. Более того, в VerboseListTest есть семь (!) тестовых методов, которые используют MSG. Таким образом, у нас будет 14 вхождений "something", верно? Да, верно, и, вероятно, поэтому один из авторов этого тестового случая ввел константу, чтобы избавиться от дублирования. Кстати, @Happy-Neko сделал это в pull request #513, @carlosmiranda просмотрел код, и я утвердил изменения. Таким образом, три человека сделали/утвердили эту ошибку, включая меня самого.

Итак, каков правильный подход, который избежит дублирования кода, но в то же время не приведет к связыванию? Вот он:

Эти литералы должны быть разными. Это то, что говорит любой статический анализатор, когда он видит "something" в таких многих местах. Он задает нам вопрос — почему они одинаковые? Действительно ли так важно использовать "something" везде? Почему нельзя использовать разные литералы? Конечно, мы можем. И мы должны.

Главное, что каждый тестовый метод должен иметь свой набор данных и объектов. Они никогда не должны быть общими между тестовыми методами. Тестовые методы всегда должны быть независимыми и не иметь ничего общего.

Имея это в виду, мы можем легко заключить, что методы типа setUp() или любые общие переменные в тестовых классах являются злом. Их нельзя использовать и просто не должно быть. Я думаю, что их изобретение в JUnit причинило много вреда коду на Java.

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-15 at 06:27

sixnines availability badge   GitHub stars