A Few Thoughts on Unit Test Scaffolding

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

Когда я начинаю повторяться в методах модульного тестирования, создавая одни и те же объекты и подготавливая данные для запуска теста, я разочаровываюсь в своем дизайне. Длинные методы тестирования с большим количеством дублирования кода выглядят неправильно. Чтобы упростить их и сократить, существует в основном два варианта, по крайней мере в Java: 1) закрытые свойства, инициализированные через @Before и @BeforeClass, и 2) закрытые статические методы. Оба варианта выглядят для меня противоречащими ООП, и я думаю, что есть альтернатива. Позвольте мне объяснить.

JUnit официально предлагает тестовый фикстур:

Я думаю, что очевидно, что делает этот тест. Сначала в prepare() он создает “тестовый фикстур” типа Folder. Это используется во всех трех тестах в качестве аргумента для конструктора Metrics. Реальным классом, который здесь тестируется, является Metrics, а this.folder - это то, что нам нужно для его тестирования.

Что не так с этим тестом? Есть одна серьезная проблема: связность между тестовыми методами. Тестовые методы (и все тесты вообще) должны быть полностью изолированы друг от друга. Это означает, что изменение одного теста не должно влиять на другие. В этом примере это не так. Когда я хочу изменить тест countsWords(), мне приходится изменять внутренности before(), что повлияет на другой метод в “классе” теста.

Со всем уважением к JUnit, идея создания тестовых фикстур в @Before и @After неправильна, главным образом потому, что это побуждает разработчиков к связыванию тестовых методов.

Вот как мы можем улучшить наш тест и изолировать тестовые методы:

Выглядит лучше сейчас? Мы еще не достигли цели, но наши тестовые методы теперь полностью изолированы. Если я хочу изменить один из них, я не буду влиять на остальные, потому что передаю все конфигурационные параметры в частный статический вспомогательный (!) метод folder().

Вспомогательный метод, да? Да, что-то нехорошее.

Основная проблема этого дизайна, хотя он намного лучше предыдущего, заключается в том, что он не предотвращает дублирование кода между “классами” тестов. Если мне понадобится похожая фиктивная структура тестового типа Folder в другом тестовом случае, мне придется переместить этот статический метод туда. Или, что еще хуже, мне придется создать вспомогательный класс. Да, в объектно-ориентированном программировании нет ничего хуже вспомогательных классов.

Гораздо лучшим решением было бы использование “фальшивых” объектов вместо частных статических вспомогательных средств. Вот как. Сначала мы создаем фальшивый класс и помещаем его в src/main/java. Этот класс может использоваться в тестах и также в производственном коде, если необходимо (Fk для “фейка”):

Вот как будет выглядеть наш тест сейчас:

Что вы думаете? Разве это не лучше, чем то, что предлагает JUnit? Разве это не более многоразовое и расширяемое, чем утилитарные методы?

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

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-28 at 14:41

sixnines availability badge   GitHub stars