Много раз слышал о том, что размещать тесты в Go принято в папке с тестируемым кодом. Слышал разные аргументы: "это идеоматично", "проще". Но долгое время мне казалось, что размещение в отдельном пакете делает проект чище, структурированнее. В пакете код не путается с тестами, при импорте пользователи пакета не будут тащить моки, лишние зависимости, используемые только в целях тестирования. Поэтому бинарники пользователей будут в разы "тоньше". Но тут в руки попалась книга "Go in Practice" авторов Matt Butcher и Matt Farina в которой утверждается:
"Testing code goes with source code
We see two common mistakes when pro grammers are learning to test Go code. The first is to try to put the test files in their own directory. Popular testing tools in other languages do this, but Go doesn’t. Test files belong in the same directory as the code they test.
The second mistake is to try to put the tests in a different package (package hello_test or something similar). Tests should go in the same package as the code they test. This makes it possible to test unexported code as well as just the public API."
До сих пор не пойму, что именно в этих утверждениях вызывало у меня доверие. Но я засомневался в своих убеждениях. Что ж, настало время разобраться в истине. Взял перенёс в один из своих пакетов тесты и собрал проект с новой версией этого пакета. Оказалось, что размер увеличился всего на 9 байт! Глянул на свой пакет в папке vendor, а там нет ни моков, ни тестов. Только сам файл с функционалом!
Стало интересно, что думает на эту тему искусственный интеллект. Сформировал промпт:
Является ли ошибкой выделение тестов в отдельный пакет или папку в проектах написанных на Go? Например, авторы книги "Go in practice" Matt Butcher, Matt Farina утверждают, что это ошибка. Цитата: "We see two common mistakes when programmers are learning to test Go code. The first is to try to put the test files in their own directory. Popular testing tools in other languages do this, but Go doesn’t. Test files belong in the same directory as the code they test. The second mistake is to try to put the tests in a different package (package hello_test or something similar). Tests should go in the same package as the driven development) even hinge on the authoring of test cases". Подробно объясни, почему это может быть ошибкой с точки зрения идеологии и философии языка Go.
ChatGPT ответил:
В языке Go действительно существует строгая философия относительно организации тестов, и выделение тестов в отдельный пакет или директорию может считаться нарушением этой философии. Давайте рассмотрим несколько причин, по которым это может рассматриваться как ошибка:
1. Согласованность и простота структуры проекта
Go придерживается принципа, что тесты должны находиться рядом с кодом, который они тестируют. Это значит, что разработчик, работающий с определенной частью кода, сразу видит и связанные с ним тесты. Это способствует более простому пониманию и поддержанию кода.
Преимущества:
- Упрощает навигацию: программист не должен искать тесты в другом месте, а всегда может найти их в одном месте.
- Упрощает понимание кода: читая код, можно сразу видеть соответствующие тесты и логику, проверяющую его.
2. Упрощение эксплуатации тестов
Когда тесты находятся в том же пакете (или той же директории), что и код, который они проверяют, это позволяет легко выполнять тесты с помощью команды go test. Если тесты находятся в отдельной директории, их выполнение становится более сложным, требуется выполнять дополнительные шаги или указывать больше параметров.
3. Именование и применение пакетов
Go использует концепцию пакетов, которые служат для группировки кода и обеспечения его модульности. Когда тесты находятся в том же пакете, они могут использовать неприватные (неначинающиеся с заглавной буквы) функции и переменные из тестируемого кода. Это позволяет эффективно тестировать внутренние детали реализации без необходимости делать их публичными или создавать дополнительные интерфейсы.
Преимущества:
- Более простое тестирование внутренних функций и логики, которые могут быть необходимы для тщательной проверки.
- Уменьшение необходимости в использовании “шумного” кода для создания и тестирования абстракций.
4. Правило одного фронтенда
Go следует принципу, что каждый файл должен иметь одно предназначение. Этот принцип также касается тестов. Выделяя тесты в отдельный пакет или директорию, вы фактически создаете избыточное разделение, что затрудняет понимание контекста и назначение тестов.
5. Поддержка жесткой типизации и явности
Одной из целей Go является поддержка жесткой типизации и явности. Тестирование должно быть частью нормального рабочего процесса разработки. Если тесты находятся в другом пакете или папке, это может создавать иллюзию, что они не являются “первоклассными” гражданами в проекте и не должны рассматриваться наравне с основным кодом.
Заключение
Хотя это не является строгой ошибкой с точки зрения компилятора Go (он все равно будет компилировать и выполнять тесты, даже если они находятся в другом пакете или директории), это противоречит проектной философии языка. Это может затруднить понимание и поддержку кода другими разработчиками, а также замедлить общий процесс разработки и тестирования. Поэтому рекомендуется следовать традициям и практикам, установленным сообществом Go, придерживаясь размещения тестов в тех же пакетах и каталогах, что и тестируемый код.
DeepSeek ответил:
В языке Go существует специфический подход к организации тестов, который отличается от подходов в некоторых других языках программирования. Авторы книги “Go in Practice” Matt Butcher и Matt Farina указывают на два распространенных “ошибки” при организации тестов в Go:
- Размещение тестовых файлов в отдельной папке.
- Размещение тестов в отдельном пакете (например,
package hello_test).
Давайте разберем, почему эти подходы могут считаться ошибками с точки зрения философии и идеологии языка Go.
1. Тестовые файлы должны находиться в той же папке, что и код
В Go тестовые файлы располагаются в той же директории, что и основной код, который они тестируют. Это обусловлено несколькими причинами:
- Повышение читаемости и поддерживаемости: Когда тесты находятся рядом с кодом, разработчику проще находить и обновлять их. Это позволяет быстрее понять, как работает модуль, и какие тесты для него написаны.
- Простота импорта и зависимости: Тесты используют тот же пакет, что и основной код, что упрощает доступ к внутренним структурам и функциям. Это особенно полезно для тестирования внутренней логики, которая не экспортируется (начинается с маленькой буквы).
- Следование идиоматическому Go: Go известен своей простотой и минимализмом. Размещение тестов в той же папке — это идиоматический подход, который помогает поддерживать код в соответствии с философией языка.
Таким образом, отделение тестов в другую папку может привести к усложнению навигации по проекту и нарушению принципов простоты и ясности, которые пропагандирует Go.
2. Тесты должны находиться в том же пакете, что и основной код
Тесты в Go обычно пишутся в том же пакете, что и основной код. Это позволяет:
- Тестировать неэкспортированные элементы: Если тесты находятся в том же пакете, они могут напрямую обращаться к неэкспортированным функциям, переменным и структурам. Это важно для полноты тестирования, так как внутренняя логика тоже требует проверки.
- Упрощение тестирования: Использование одного пакета устраняет необходимость создания сложных зависимостей или моков для тестирования внутренних компонентов.
- Следование принципам модульности: Go поощряет создание небольших, автономных пакетов. Если тесты находятся в том же пакете, это помогает поддерживать модульность и изоляцию кода.
Если же тесты помещаются в отдельный пакет (например, package hello_test), разработчик теряет возможность тестировать неэкспортированные элементы. Это может привести к неполному тестированию и нарушению принципа “тестировать все, что может сломаться”.
Почему это важно с точки зрения философии Go?
Философия Go строится на принципах простоты, ясности и минимализма. Размещение тестов в той же папке и пакете, что и основной код, соответствует этим принципам:
- Простота: Нет необходимости создавать сложные структуры папок или пакетов для тестов. Все находится в одном месте.
- Ясность: Легко понять, какие тесты относятся к какому коду, и где их искать.
- Минимализм: Go избегает избыточности. Разделение тестов на отдельные папки или пакеты добавляет ненужную сложность.
Когда можно использовать отдельный пакет для тестов?
В некоторых случаях использование отдельного пакета для тестов (например, package hello_test) оправдано:
- Интеграционное тестирование: Если тесты проверяют взаимодействие между несколькими пакетами, их можно вынести в отдельный пакет.
- Тестирование публичного API: Если нужно проверить, как пакет используется извне, тесты могут быть написаны в отдельном пакете, чтобы имитировать поведение внешнего клиента.
Однако даже в этих случаях рекомендуется минимально отклоняться от идиоматического подхода.
Заключение
Авторы книги “Go in Practice” правы: размещение тестов в отдельной папке или пакете может считаться ошибкой с точки зрения философии Go. Это нарушает принципы простоты, ясности и минимализма, которые лежат в основе языка. Чтобы писать идиоматический код на Go, тесты следует располагать в той же папке и пакете, что и основной код.
Янчарук А.В.