EnglishРусский Map

T.Context в тестировании на Go

title
T.Context в тестировании на Go
type
summary
summary
Короткая заметка Джонатана Холла о том, когда стоит использовать testing.T.Context из Go 1.24, а когда нет.
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 показан шаблон с конструктором теста, который владеет такими ресурсами отдельного теста и уничтожает их вместе с ним.