сеньорчикОткрыть в Telegram
← все вопросывопросы для собеседований · Веб и тесты на Go

Вопросы по веб-сервисам и тестам на Go на собеседовании

Веб-часть Go проверяют через стандартную библиотеку: почти всё решается net/http без фреймворков, и это ожидают увидеть в ответе. Тесты спрашивают отдельно, потому что табличный стиль и детектор гонок это часть культуры языка.

62 вопросов в банке·10 подтем·ниже разбор 9

Из чего состоит тема

Так тема разложена в тренажёре: движок ведёт прогресс по каждой подтеме отдельно и возвращает те, где вы ошибаетесь.

Разборы подтем

Конспект по каждой: что это, как отвечать вслух, на чём валятся, плюс вопросы для самопроверки.

Примеры вопросов с разбором

  1. #go_bench_race1 / 9
    Как выглядит бенчмарк в пакете testing?
    A)func BenchmarkXxx(b *testing.B) с циклом for i := 0; i < b.N; i++
    B)Обычная func Xxx() с ручным замером через time.Now в начале и в самом конце функции
    C)Функция теста с аннотацией @Benchmark, поставленной над её объявлением сверху
    D)Флаг -time у go run, который печатает общее время выполнения программы в конце
    показать ответ и разбор
    +A)func BenchmarkXxx(b *testing.B) с циклом for i := 0; i < b.N; i++

    // разбор: Бенчмарк — func BenchmarkXxx(b *testing.B) в _test.go, где измеряемую операцию гоняют в цикле for i := 0; i < b.N; i++. Число итераций b.N подбирает сам фреймворк, наращивая его, пока замер не станет статистически стабильным, и печатает ns/op (и B/op, allocs/op при -benchmem). Запуск: go test -bench=. Спот-чек: BenchmarkAdd отработал b.N раз.

  2. #go_mocks2 / 9
    Как в Go принято подменять зависимость (например хранилище) в тесте?
    A)Патчить нужные функции прямо в рантайме специальной monkey-patch библиотекой
    B)Глобальной переменной-флагом, который прод-код проверяет в тестовом режиме работы
    C)Через рефлексию подменять поля структуры на лету прямо перед вызовом метода
    D)Принимать зависимость через интерфейс и передавать в тесте свою реализацию
    показать ответ и разбор
    +D)Принимать зависимость через интерфейс и передавать в тесте свою реализацию

    // разбор: Идиома Go — dependency injection через интерфейс: код зависит от мелкого интерфейса (Store с нужными методами), а в тесте передают свою простую реализацию-заглушку (или сгенерированный мок). Никакого monkey-patching и рефлексии: подмена статически типобезопасна и явна. Отсюда снова принцип «маленькие интерфейсы у потребителя» — они и делают код тестируемым.

  3. #go_modules_tooling3 / 9
    Что задаёт файл go.mod и команда go mod init?
    A)Список тестов, которые прогонит команда go test в этом каталоге проекта
    B)Путь модуля, версию Go и его прямые зависимости с версиями
    C)Конфигурацию линтера и правил форматирования кода для всего проекта
    D)Скрипты сборки, запуска и прочие команды проекта, ровно как package.json в Node.js
    показать ответ и разбор
    +B)Путь модуля, версию Go и его прямые зависимости с версиями

    // разбор: go.mod — манифест модуля: строка module (путь импорта модуля), директива go (версия языка) и require со списком прямых зависимостей и их версий. go mod init <path> создаёт его. Точные версии всего дерева (включая транзитивные) и их хеши фиксируются в go.sum. Это не скрипты сборки и не конфиг линтера.

  4. #go_table_driven4 / 9
    Как выглядит идиоматичный table-driven тест в Go?
    A)Отдельная самостоятельная тест-функция на каждый отдельный набор входных данных
    B)Срез структур-кейсов, по нему цикл с t.Run(name, ...) на каждый
    C)Внешний JSON-файл с кейсами, который читают рефлексией во время прогона теста
    D)Параметризованный класс с провайдером данных, указанным в специальной аннотации
    показать ответ и разбор
    +B)Срез структур-кейсов, по нему цикл с t.Run(name, ...) на каждый

    // разбор: Идиома: срез анонимных структур {name, вход, ожидание}, а затем цикл, где каждый кейс запускается как подтест t.Run(c.name, func(t *testing.T){ ... }). Плюсы: добавить случай — одна строка; подтесты именованы (видно, какой упал) и запускаются выборочно go test -run Test/case. Спот-чек: подтесты pos/zero/neg прошли по отдельности.

  5. #go_testing_basics5 / 9
    Как выглядит тест в стандартном пакете testing и как его запустить?
    A)func check() с assert из внешней библиотеки, а запускается всё обычным go run
    B)Метод класса TestCase с методами setUp и tearDown, запуск через внешний раннер
    C)func TestXxx(t *testing.T) в файле _test.go; запуск go test
    D)Функция с аннотацией @Test сверху, запускаемая командой go verify
    показать ответ и разбор
    +C)func TestXxx(t *testing.T) в файле _test.go; запуск go test

    // разбор: Тест — функция func TestXxx(t *testing.T) в файле, чьё имя оканчивается на _test.go. Провал сообщают через t.Error/t.Errorf (тест продолжается) или t.Fatal (останавливает эту тест-функцию). Запуск — go test (весь пакет) или go test -run. Никаких аннотаций, классов и внешних раннеров: тестовый фреймворк встроен в тулчейн.

  6. #go_http_client6 / 9
    После resp, err := http.Get(url) и проверки ошибки — что обязательно сделать с resp.Body?
    A)Ничего: тело закрывается сборщиком мусора автоматически
    B)Закрыть через defer resp.Body.Close(), иначе течёт соединение
    C)Вызвать resp.Body.Flush() перед чтением содержимого
    D)Скопировать тело в строку — только тогда соединение освободится
    показать ответ и разбор
    +B)Закрыть через defer resp.Body.Close(), иначе течёт соединение

    // разбор: Тело ответа — открытый поток поверх TCP-соединения; его обязательно закрывать: defer resp.Body.Close() сразу после проверки err. Иначе соединение не вернётся в пул и утечёт (в цикле быстро упрёшься в лимит). GC его вовремя не закроет. Для переиспользования keep-alive тело ещё и стоит дочитать до конца (io.Copy в io.Discard) перед Close.

  7. #go_json7 / 9
    json.Marshal сериализует структуру. Какие поля попадут в JSON?
    A)Все поля подряд, включая приватные — рефлексия добирается и до них
    B)Только поля с явным тегом json, прочие пропускаются
    C)Поля в алфавитном порядке имён, приватные в конце
    D)Только экспортируемые поля; имя берётся из тега json или самого поля
    показать ответ и разбор
    +D)Только экспортируемые поля; имя берётся из тега json или самого поля

    // разбор: encoding/json через рефлексию видит ТОЛЬКО экспортируемые (с заглавной буквы) поля — неэкспортируемые молча пропускаются (частый баг: поле с маленькой буквы «не сериализуется»). Имя ключа берётся из тега json, иначе — имя поля как есть. omitempty убирает пустые. Спот-чек: age (с маленькой) в вывод не попал.

  8. #go_middleware8 / 9
    Как в net/http идиоматично устроено middleware (логирование, auth)?
    A)Через глобальные хуки, которые регистрируют в поле http.Server.OnRequest
    B)Функция, оборачивающая http.Handler в новый Handler с логикой до/после
    C)Наследованием от базового http.BaseHandler с переопределением метода
    D)Отдельным потоком-перехватчиком перед каждым обработчиком
    показать ответ и разбор
    +B)Функция, оборачивающая http.Handler в новый Handler с логикой до/после

    // разбор: middleware в Go — функция func(next http.Handler) http.Handler: она возвращает новый Handler, который делает что-то ДО (лог, проверка токена), вызывает next.ServeHTTP и что-то ПОСЛЕ. Их выстраивают в цепочку, оборачивая друг в друга. Никаких глобальных хуков или наследования — только композиция мелкого интерфейса http.Handler.

  9. #go_net_http9 / 9
    Какая сигнатура у обработчика HTTP в net/http?
    A)func(*http.Request) http.Response — принимает запрос и возвращает готовый ответ
    B)func(w http.ResponseWriter, r *http.Request) — ответ пишем в w
    C)func(ctx context.Context) ([]byte, error) — возвращаем тело и ошибку
    D)func(req, resp map[string]any) — запрос и ответ как словари
    показать ответ и разбор
    +B)func(w http.ResponseWriter, r *http.Request) — ответ пишем в w

    // разбор: Обработчик в net/http имеет вид func(w http.ResponseWriter, r *http.Request): читаешь запрос из r, а ответ ПИШЕШЬ в w (заголовки, статус, тело) — функция ничего не возвращает. Такую функцию регистрируют через http.HandleFunc или оборачивают в http.HandlerFunc, которая реализует интерфейс http.Handler с методом ServeHTTP.

это 9 из 62

Ещё 53 вопросов по теме — в тренажёре, с движком повторения

Прочитать разбор и ответить самому — разные навыки. В Сеньорчике вопросы идут сессиями, а движок возвращает подтемы, где вы ошибаетесь, пока они не начнут отскакивать. Бесплатно, лимит по энергии.

Частые вопросы