Правильное именование пакетов для тестирования на языке Go
Я видел несколько различных стратегий именования тестовых пакетов в Go и хотел узнать, что за плюсы и минусы каждого из них и какой я должен использовать.
Стратегия 1:
Имя файла: github.com/user/myfunc.go
package myfunc
Имя тестового файла: github.com/user/myfunc_test.go
package myfunc
См. bzip2 для примера.
Стратегия 2:
Имя файла: github.com/user/myfunc.go
package myfunc
Имя тестового файла: github.com/user/myfunc_test.go
package myfunc_test
import (
"github.com/user/myfunc"
)
См. wire для примера.
Стратегия 3:
Имя файла: github.com/user/myfunc.go
package myfunc
Имя тестового файла: github.com/user/myfunc_test.go
package myfunc_test
import (
. "myfunc"
)
См. strings для примера.
Стандартная библиотека Go Go, похоже, использует смесь стратегий 1 и 2. Какую из трех я должен использовать? Это боль, добавляющая package *_test
к моим тестовым пакетам, так как это означает, что я не могу проверить свои частные методы пакета, но, возможно, есть скрытое преимущество, о котором я не знаю?
Ответы
Ответ 1
Фундаментальное различие между тремя перечисленными вами стратегиями заключается в том, находится ли тестовый код в том же пакете, что и тестируемый код. Решение использовать package myfunc
или package myfunc_test
в тестовом файле зависит от того, хотите ли вы выполнить white-box или black-box.
Нет ничего плохого в использовании обоих методов в проекте. Например, вы могли бы иметь myfunc_whitebox_test.go
и myfunx_blackbox_test.go
.
Сравнение пакетов тестового кода
- Тестирование Black-box: Используйте
package myfunc_test
, который гарантирует, что вы используете только экспортированные идентификаторы.
- Тестирование белого ящика: Используйте
package myfunc
, чтобы у вас был доступ к неэкспортированным идентификаторам. Хорошо для модульных тестов, требующих доступа к неэкспортированным переменным, функциям и методам.
Сравнение стратегий, перечисленных в вопросе
- Стратегия 1: В файле
myfunc_test.go
используется package myfunc
. В этом случае тестовый код в myfunc_test.go
будет в том же пакете, что и код, протестированный в myfunc.go
, который myfunc
в этом примере.
- Стратегия 2: В файле
myfunc_test.go
используется package myfunc_test
- в этом случае тестовый код в myfunc_test.go
"будет скомпилирован как отдельный пакет, а затем связан и запущен с основным тест двоичный." [Источник: строки 58-59 в test.go исходный код]
- Стратегия 3: В файле
myfunc_test.go
используется package myfunc_test
, но импортируется myfunc
с использованием точечной нотации. Это вариант стратегии 2, но использует точечную нотацию для импорта myfunc
.
Ответ 2
Это зависит от объема ваших тестов. Тесты высокого уровня (интеграция, приемка и т.д.), Вероятно, должны быть размещены в отдельном пакете, чтобы гарантировать, что вы используете пакет через экспортированный API.
Если у вас большой пакет с множеством внутренних компонентов, которые нужно подвергнуть тестированию, используйте тот же пакет для своих тестов. Но это не приглашение для ваших тестов на доступ к любому частному состоянию. Это сделало бы рефакторинг кошмаром. Когда я пишу structs в go, я часто реализую интерфейсы. Это те методы интерфейса, которые я вызываю из своих тестов, а не все вспомогательные методы/функции по отдельности.
Ответ 3
Вы должны использовать стратегию 1, когда это возможно. Вы можете использовать специальное имя пакета foo_test
, чтобы избежать циклов импорта, но в основном там, поэтому стандартная библиотека может быть протестирована с помощью того же механизма. Например, strings
не может быть протестирован со стратегией 1, поскольку пакет testing
зависит от strings
. Как вы сказали, со стратегией 2 или 3 у вас нет доступа к частным идентификаторам пакета, поэтому обычно лучше не использовать его, если вам не нужно.
Ответ 4
Я не могу комментировать, потому что у меня нет 50 представителей... (как хромает). Итак, чтобы ответить на вопрос @PickBoy выше:
Я видел, что сильный пакет использует Стратегию 3, но я не могу понять, в чем смысл? - 2 дня назад
Я раздвоил пакет и внес изменения, и теперь все мои тесты пытаются импортировать исходное хранилище вместо моего раздвоенного пакета. В Стратегии 3 мне не нужно менять "github.com/original/link" на "github.com/my/fork", потому что он просто ссылается на "." вместо.