T.Context в тестировании на Go
- title
- T.Context в тестировании на Go
- type
- summary
- summary
- Короткая заметка Джонатана Холла о том, когда стоит использовать testing.T.Context из Go 1.24, а когда нет.
- sources
- t-context-go-testing
- tags
- golang, testing, context
- created
- 2026-04-25
- updated
- 2026-09-13
- lang
- ru
- translation_of
- t-context-go-testing
- source_updated
- 2026-09-13
- translated
- 2026-09-14
- translator
- lllm/antigravity/gemini-3.7-flash-medium
Короткая заметка в формате ответа на вопрос читателя от Джонатана Холла на boldlygo-tech, продолжающая его предыдущую статью о context.TODO(). Бруно Схаатсберген (Bruno Schaatsbergen) спросил, почему Холл не порекомендовал новые методы T.Context() / B.Context() / F.Context() из Go 1.24. Честный ответ Холла: он о них забыл. Развёрнутый ответ и составляет основное содержание публикации.
Что делают эти методы
В Go 1.24 в testing.T, testing.B и testing.F появились методы Context(). Каждый из них возвращает context.Context, отмена которого привязана к времени жизни конкретного теста, бенчмарка или fuzz-таргета. Когда тест завершается (или отменяется), контекст отменяется, так что любые goroutine, запущенные с этим контекстом, автоматически получают сигнал отмены без необходимости вручную вызывать очистку.
Это заменяет привычный шаблон с написанием ctx, cancel := context.WithCancel(context.Background()) и t.Cleanup(cancel) в каждом тесте, где нужен контекст.
Рекомендация Холла
Для большинства обычных тестов используйте T.Context(). Это правильный вариант по умолчанию: он ограничен областью видимости теста, очищается автоматически и не требует шаблонного кода.
Исключение - всё, чьё время жизни шире отдельного теста. Холл приводит два примера:
- Тестовый сервер, запущенный в
TestMainили в общем коде инициализации и используемый сразу несколькими тестами - Контейнер Docker'а, запущенный через Testcontainers или аналогичный инструмент и разделяемый между всеми тестами в бинарнике
Если использовать для них T.Context(), они остановятся сразу после завершения первого же обратившегося к ним теста, что для разделяемых ресурсов не подходит. В таких случаях используйте context.Background() (или контекст более высокого уровня, живущий на уровне пакета или TestMain) и управляйте отменой явно.
Общее правило
Время жизни контекста должно совпадать с временем жизни владеющего им ресурса. T.Context() владеет "только этим тестом"; если нужно, чтобы ресурс пережил тест, потребуется контекст, чей владелец живёт так же долго.
В Rust к этой же проблеме подходят иначе, переворачивая предпосылку об исключении для Testcontainers. В delightful-integration-tests-rust контейнер на каждый тест становится дешёвым решением по умолчанию, поэтому между тестами бинарника ничего не разделяется и ничто не переживает отдельный тест - что устраняет проблему, о которой предупреждает Холл, вместо того чтобы её обходить. В anatomy-of-a-test показан шаблон с конструктором теста, который владеет такими ресурсами отдельного теста и уничтожает их вместе с ним.