> Писал ли юнит-тесты для bash-скриптов (Go)

Уровень: middle · Роль: backend · Категория: Технические вопросы

Компании: Wildberries

Стек: Go

> Пример ответа

Короткий ответ

Да, писал. Для bash-скриптов использовал Bats (Bash Automated Testing System) и shellcheck для статического анализа. Основной фокус - тестирование логики скриптов: обработка аргументов, exit codes, работа с файлами и переменными окружения. В Go-проектах bash-скрипты обычно покрывают CI-задачи, миграции или деплой, поэтому тесты писал для критичных сценариев, а не для всего подряд.

Подробное объяснение

Bash-скрипты в Go-проектах чаще всего выполняют вспомогательные функции: сборка, запуск тестов, генерация кода, работа с Docker. Тестировать их нужно, потому что ошибка в таком скрипте может сломать CI или деплой.

Основные подходы:

  • Bats - фреймворк для юнит-тестов bash. Позволяет писать тесты в стиле TAP (Test Anything Protocol), проверять stdout/stderr, exit codes.
  • shellcheck - не тесты, но обязательный инструмент для статического анализа. Ловит синтаксические ошибки, неиспользуемые переменные, проблемы с quoting.
  • mock-зависимости - если скрипт вызывает внешние команды (docker, git, curl), их можно замокать через PATH или функции-заглушки.
  • тестирование через set -euo pipefail - это не тест, но помогает сделать скрипт предсказуемым и тестируемым.

Пример структуры:

BASH
#!/usr/bin/env bats
setup() {
export TEST_DIR=$(mktemp -d)
}
teardown() {
rm -rf "$TEST_DIR"
}
@test "скрипт возвращает 0 при успешном выполнении" {
run ./deploy.sh --dry-run
[ "$status" -eq 0 ]
}
@test "скрипт обрабатывает отсутствие аргумента" {
run ./deploy.sh
[ "$status" -ne 0 ]
[[ "$output" =~ "usage:" ]]
}

На практике

В реальных Go-проектах я покрывал тестами:

  • скрипты миграций БД (проверка порядка выполнения, идемпотентность);
  • скрипты генерации кода (protoc, swagger);
  • CI-скрипты (проверка, что скрипт корректно обрабатывает отсутствие переменных окружения);
  • скрипты деплоя (dry-run режим, проверка валидности конфигов).

Важный момент - не тестировать сам bash, а тестировать логику скрипта. Если скрипт становится слишком сложным для тестирования, это сигнал переписать его на Go (особенно если это часть основного приложения).

Пример кода

BASH
#!/usr/bin/env bats
load 'test_helper'
@test "обработка флага --verbose" {
run ./build.sh --verbose
[ "$status" -eq 0 ]
[[ "$output" == *"Building..."* ]]
}
@test "скрипт падает при отсутствии GOOS" {
unset GOOS
run ./build.sh
[ "$status" -ne 0 ]
[[ "$output" == *"GOOS is required"* ]]
}
@test "мок docker-команды" {
# подменяем docker на заглушку
export PATH="$BATS_TEST_DIRNAME/mocks:$PATH"
run ./deploy.sh
[ "$status" -eq 0 ]
[[ "$output" == *"docker build"* ]]
}

Как отвечать на собеседовании

Начни с конкретного примера: "Да, для скрипта миграций в Go-проекте использовал Bats". Затем опиши, что именно тестировал и почему. Упомяни, что не покрываешь всё подряд - только критичные пути. Если спросят про альтернативы, скажи, что для сложной логики предпочитаешь переписать на Go, а bash оставить для простых задач.

Что проверяет интервьюер

Интервьюер хочет понять:

  • умеешь ли ты писать тесты не только для основного кода, но и для инфраструктурных скриптов;
  • понимаешь ли границы применимости bash-тестов;
  • знаешь ли инструменты экосистемы (Bats, shellcheck);
  • умеешь ли мокать внешние зависимости.

Типичные ошибки

  • Тестировать только happy path, игнорируя обработку ошибок.
  • Пытаться тестировать bash-скрипты через Go-тесты (это возможно, но неудобно и медленно).
  • Не использовать set -euo pipefail - без этого тесты могут давать ложноположительные результаты.
  • Мокать слишком много - если скрипт полностью замокан, тест не проверяет реальное поведение.
  • Забывать про teardown - временные файлы и переменные окружения должны очищаться.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью