Modern QA2026Bash-скриптинг для QA
Join

Course12 Programming for QA

Foundations · Chapter 12

Bash-скриптинг для QA

Updated Jul 2026

Bash — это клей, связывающий CI/CD-пайплайны, тестовые окружения и рабочие процессы автоматизации. Вам не нужно быть экспертом по shell-скриптингу, но вы должны уверенно писать скрипты для настройки тестовых окружений, запуска тест-сьютов и обработки результатов.

Базовый шаблон скрипта

Каждый bash-скрипт должен начинаться с этого:

#!/bin/bash
set -euo pipefail
Флаг Значение Почему это важно
set -e Завершение при любой ошибке Предотвращает тихое продолжение скрипта после сбоя
set -u Ошибка при неопределённых переменных Ловит опечатки в именах переменных
set -o pipefail Конвейер падает, если любая команда в нём падает `cmd1

Без этих флагов скрипт может упасть незаметно — сброс базы данных не удался, но тесты всё равно запускаются на устаревших данных.

Скрипт настройки тестового окружения

#!/bin/bash
set -euo pipefail

echo "=== Resetting test database ==="
psql -h localhost -U testuser -d testdb -f reset.sql

echo "=== Starting test server ==="
npm run start:test &
SERVER_PID=$!

# Wait for server to be ready (up to 30 seconds)
echo "=== Waiting for server ==="
for i in {1..30}; do
    if curl -s http://localhost:3000/health > /dev/null 2>&1; then
        echo "Server ready after ${i}s"
        break
    fi
    if [ $i -eq 30 ]; then
        echo "ERROR: Server did not start within 30s"
        kill $SERVER_PID 2>/dev/null || true
        exit 1
    fi
    sleep 1
done

echo "=== Running tests ==="
pytest tests/ --junitxml=results.xml
TEST_EXIT=$?

echo "=== Stopping server ==="
kill $SERVER_PID 2>/dev/null || true

echo "=== Done (exit code: $TEST_EXIT) ==="
exit $TEST_EXIT

Ключевые паттерны в этом скрипте

  • Фоновый процесс (& и $!): запуск сервера в фоне и захват его PID
  • Цикл проверки здоровья: ожидание готовности сервера перед запуском тестов
  • Захват кода выхода ($?): сохранение кода выхода тестов перед очисткой
  • Очистка при завершении: остановка сервера независимо от результата тестов
  • Корректная обработка ошибок: kill $PID 2>/dev/null || true не падает, если процесс уже завершён

Переменные окружения

Переменные окружения параметризуют запуски тестов для разных окружений.

#!/bin/bash
set -euo pipefail

# Default values with fallback
BASE_URL="${API_BASE_URL:-http://localhost:3000}"
DB_HOST="${TEST_DB_HOST:-localhost}"
WORKERS="${TEST_WORKERS:-4}"

echo "Running tests against: $BASE_URL"
echo "Database: $DB_HOST"
echo "Workers: $WORKERS"

API_BASE_URL="$BASE_URL" pytest tests/ -n "$WORKERS"
# Run against different environments
API_BASE_URL=https://staging.example.com ./run_tests.sh
API_BASE_URL=https://production.example.com TEST_WORKERS=1 ./run_tests.sh

Полезные конструкции Bash

Условное выполнение

# Run smoke tests first, only run full suite if smoke passes
pytest tests/smoke/ && pytest tests/full/

# Run cleanup regardless of test outcome
pytest tests/ || true
./cleanup.sh

Операции с файлами и директориями

# Create test output directory
mkdir -p test-results/screenshots

# Check if file exists
if [ -f "test-data.sql" ]; then
    psql testdb < test-data.sql
else
    echo "WARNING: test-data.sql not found"
fi

# Clean up old test artifacts
find test-results/ -name "*.png" -mtime +7 -delete

Циклы и параллельное выполнение

# Run tests for each environment
for env in staging production; do
    echo "Testing $env..."
    API_BASE_URL="https://${env}.example.com" pytest tests/smoke/ \
        --junitxml="results-${env}.xml"
done

# Parallel execution with xargs
echo "staging production sandbox" | tr ' ' '\n' | \
    xargs -P 3 -I {} bash -c 'API_BASE_URL="https://{}.example.com" pytest tests/smoke/'

Обработка строк

# Extract test count from pytest output
RESULT=$(pytest tests/ --tb=no 2>&1 | tail -1)
echo "Result: $RESULT"
# "5 passed, 2 failed in 12.3s"

# Check if tests failed
if echo "$RESULT" | grep -q "failed"; then
    echo "TESTS FAILED"
    exit 1
fi

Скрипты для CI/CD-пайплайнов

Вспомогательный скрипт для GitHub Actions

#!/bin/bash
set -euo pipefail

# Parse command line arguments
SUITE="${1:-smoke}"
ENVIRONMENT="${2:-staging}"

echo "Running $SUITE tests against $ENVIRONMENT"

# Set environment-specific variables
case "$ENVIRONMENT" in
    staging)
        export API_BASE_URL="https://api.staging.example.com"
        export DB_HOST="staging-db.internal"
        ;;
    production)
        export API_BASE_URL="https://api.example.com"
        export DB_HOST="prod-db.internal"
        ;;
    *)
        echo "Unknown environment: $ENVIRONMENT"
        exit 1
        ;;
esac

# Run the appropriate test suite
case "$SUITE" in
    smoke)
        pytest tests/ -m smoke --junitxml=results.xml
        ;;
    full)
        pytest tests/ --junitxml=results.xml -n 4
        ;;
    api)
        pytest tests/api/ --junitxml=results.xml
        ;;
    *)
        echo "Unknown suite: $SUITE"
        exit 1
        ;;
esac

Запуск тестов на основе Docker

#!/bin/bash
set -euo pipefail

echo "=== Building test image ==="
docker build -t test-runner -f Dockerfile.test .

echo "=== Starting dependencies ==="
docker compose -f docker-compose.test.yml up -d db redis

echo "=== Waiting for database ==="
for i in {1..30}; do
    if docker compose -f docker-compose.test.yml exec -T db pg_isready; then
        break
    fi
    sleep 1
done

echo "=== Running tests ==="
docker run --rm \
    --network test-network \
    -e API_BASE_URL=http://app:3000 \
    -e DB_HOST=db \
    -v "$(pwd)/test-results:/app/test-results" \
    test-runner pytest tests/ --junitxml=/app/test-results/results.xml

TEST_EXIT=$?

echo "=== Cleaning up ==="
docker compose -f docker-compose.test.yml down

exit $TEST_EXIT

Trap для очистки

Гарантируйте выполнение очистки даже при сбое скрипта:

#!/bin/bash
set -euo pipefail

cleanup() {
    echo "Cleaning up..."
    kill $SERVER_PID 2>/dev/null || true
    docker compose down 2>/dev/null || true
}

# Register cleanup to run on EXIT, regardless of how the script ends
trap cleanup EXIT

# Now start things up — cleanup is guaranteed to run
docker compose up -d
SERVER_PID=$!
pytest tests/

Справочник по основным конструкциям Bash

Концепция Синтаксис Почему это важно
set -euo pipefail Заголовок скрипта Ловит ошибки вместо тихого продолжения
$? Код выхода последней команды Проверка успешности инструмента перед продолжением
$! PID последнего фонового процесса Отслеживание и завершение фоновых серверов
${VAR:-default} Значение по умолчанию Параметризация без обязательности переменной
command & Запуск в фоне Запуск серверов без блокировки
trap cleanup EXIT Запуск при завершении Гарантированная очистка
xargs -P N Параллельное выполнение Запуск повторяющихся задач параллельно
2>/dev/null Подавление stderr Скрытие ожидаемых сообщений об ошибках
` true`

Практическое упражнение

Напишите bash-скрипт, который:

  1. Принимает имя окружения как аргумент командной строки (staging/production)
  2. Запускает локальную базу данных с помощью Docker
  3. Ожидает готовности базы данных (цикл проверки здоровья)
  4. Запускает дымовые тесты с pytest
  5. Захватывает код выхода тестов
  6. Очищает Docker-контейнеры (с помощью trap)
  7. Завершается с кодом выхода тестов

Ключевые выводы

  • Всегда начинайте скрипты с set -euo pipefail для отлова ошибок
  • Используйте переменные окружения для параметризации между окружениями
  • Циклы проверки здоровья предотвращают запуск тестов на неготовых сервисах
  • Захватывайте коды выхода тестов перед очисткой
  • Используйте trap cleanup EXIT для гарантии выполнения очистки
  • Bash — это клей между инструментами; изучите достаточно, чтобы оркестрировать вашу тестовую инфраструктуру

Тезис для собеседования: «Я пишу автоматизацию тестирования на Python и TypeScript. Я использую ООП для структуры фреймворка — объекты страниц с предпочтением композиции над наследованием — и функциональные паттерны вроде map/filter для преобразования данных. Я уверенно владею async/await, регулярными выражениями для парсинга логов и Bash-скриптингом для задач CI-пайплайнов. Программирование для меня — не второстепенный навык, а способ создания надёжной, поддерживаемой тестовой инфраструктуры.»