Структурирование тестов в подпапках с помощью Golang

Проект, с которого я начал go-blueprint, имеет такие модули, как:

./cmd
  ./api
    ./main.go (main)
./internal
  ...
  ./server
    ...
    ./server.go (server -> app/internal/server)
    ...
  ...
./tests
  ./handler_test.go

(Конечно, внутри гораздо больше файлов и папок internal.)

В настоящее время handler_test.go тестируется ./internal/server/server.go, и выполнение go test запустит этот тест.

Я привык проводить тесты в зеркальной структуре папок, поэтому мне хотелось бы иметь

./internal
  ./server
    ./server.go
./tests
  ./server
    ./server_test.go

При этом я могу go test app/tests/server и он работает server_test.go, но другого теста нет.

Я попробовал использовать t.Run и хотел бы импортировать тестовые функции, однако это не работает:

// ./tests/server_test.go
package tests

import (
  testServer "app/tests/server"  // ❌ could not import app/tests/server (no required module provides package "app/tests/server")

  "testing"
)

func TestServer(t *testing.T) {
  t.Run("Testing server routes", testServer.TestBaseRoutes)
}

Возможно, я не понимаю парадигму, исходящую от JUnit, PHPUnit, Jest и т. д., но я, кажется, не понимаю, как Go структурирует тесты, когда используемое приложение имеет средний размер и, возможно, содержит десятки тестовых файлов.

Возможно ли вообще то, что я делаю?

🤔 А знаете ли вы, что...
Go имеет средства для создания и использования модулей (modules) для управления зависимостями.


53
2

Ответы:

Тесты Go обычно живут в том же пакете , что и исходные файлы. Даже go-blueprint недавно исправили свои шаблоны. Если вам нравятся более идиоматические шаблоны проектов, возможно, вам стоит взглянуть на gonew.

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

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


Обратите внимание, что все тесты следует хранить вместе с исходным кодом, а не в отдельных папках, даже файлы с пакетом «_test» для тестирования «черного ящика»:

Тестовые файлы, объявляющие пакет с суффиксом «_test», будут скомпилированы как отдельный пакет, а затем скомпоновать его и запустить с основным тестовым двоичным файлом.

См. пример из стандартной библиотеки.

Почему это важно? Во-первых, ожидается, что go test <packages> проведет все тесты для целевых пакетов, во-вторых (как в примере выше) тестируемые примеры должны появиться в документации пакета.


Решено

Go эффективно различает тестирование «белого ящика» и тестирование «черного ящика». При тестировании «белого ящика» тесты находятся в том же пакете, что и код, который вы хотите протестировать, поэтому у вас есть доступ к частным внутренним компонентам. При тестировании «черного ящика», которое, к сожалению, немного недооценивается в онлайн-статьях, вы помещаете тестовый код в отдельный изолированный пакет, поэтому тестовый код имеет доступ только к общедоступному интерфейсу кода, который вы хотите протестировать.

В документации особо отмечается это разделение.

https://pkg.go.dev/testing

Цитирую:

Тестовый файл может находиться в том же пакете, что и тестируемый, или в соответствующем пакете с суффиксом «_test».

Если файл находится в отдельном пакете «_test», тестируемый пакет должен быть импортирован явно, и только его экспортированные идентификаторы могут быть использовал. Это известно как тестирование «черного ящика».

package abs_test

import (
  "testing"

  "path_to_pkg/abs" 
)

func TestAbs(t *testing.T) {
  // test public abs stuff
}

Это соглашение, которое определяет Go.

  • Тест белого ящика: поместите тесты в файл yourcode_test.go.
  • Тест «черного ящика»: поместите тесты в подкаталог yourcode_test/yourcode_test.go.