vyvchy
    Теми розділу

    09 · Git і CI/CD

    Термінал і Linux для QA

    Зміст

    Рано чи пізно кожен QA впирається в чорне вікно з курсором. Логи застосунку лежать на віддаленому сервері, куди немає доступу через браузер. CI-агент, на якому впав автотест, — це Linux-машина без графічного інтерфейсу, і єдиний спосіб зрозуміти, що сталося, — прочитати її вивід у терміналі. Тестовий стенд треба перезапустити, скинути файл із даними або підглянути, чи взагалі жива служба. Усе це робиться командами, а не мишкою.

    Ця глава — фундамент CLI, який далі мовчки припускають розділи про Docker, CI-інструменти та автотести в пайплайні. Це глава-поглиблення: якщо ви тільки будуєте базу мануального тестування, її можна пропустити при першому проході й повернутися, коли зʼявляться перші автотести або доступ до стенду. Але щойно ви торкаєтеся автоматизації чи CI — без терміналу далі буде важко: половина відповідей на питання «чому впало» живе саме там.

    Bash-мінімум: навігація, файли, права, процеси

    Bash (Bourne Again Shell) — це і командний інтерпретатор, і мова. Коли ви пишете команду й тиснете Enter, оболонка (shell) розбирає рядок, знаходить програму, запускає її та показує результат. На macOS дефолтна оболонка тепер zsh, але базовий синтаксис у них спільний, тож усе нижче працює однаково.

    Файлова система Linux — це дерево з коренем /. Ви завжди перебуваєте в якійсь директорії (робочій), і команди виконуються відносно неї.

    pwd            # print working directory — де я зараз
    ls             # список файлів
    ls -la         # усі файли (навіть приховані на крапку) + деталі
    cd /var/log    # перейти в директорію за абсолютним шляхом
    cd ..          # на рівень вгору
    cd ~           # у домашню директорію
    cd -           # назад у попередню директорію

    Різниця між абсолютним шляхом (/var/log/app.log, від кореня) і відносним (./app.log, від поточної директорії) — не дрібниця. У CI-скриптах відносний шлях залежить від того, звідки джоб запустив команду, і це класичне джерело «локально працює, у пайплайні файл не знайдено».

    Файли

    cat file.txt        # вивести весь файл
    less file.txt       # гортати великий файл (вихід — q)
    head -n 20 file     # перші 20 рядків
    cp src dst          # копіювати
    mv old new          # перемістити або перейменувати
    rm file             # видалити (без «кошика» — назавжди)
    mkdir -p a/b/c      # створити дерево директорій
    find . -name "*.log"  # знайти файли за маскою

    Головне застереження: rm не питає й не має відкату. rm -rf на неправильній директорії — це втрачені дані без надії на Ctrl+Z. На спільних стендах будьте обережні так само, як з DELETE без WHERE у базі.

    Права доступу

    Кожен файл має власника, групу і три набори прав: для власника, групи й решти. У виводі ls -l це перший стовпчик:

    -rwxr-xr-x  1 qa  staff  1240  Jul 18 10:00  run-tests.sh
    

    Перший символ — тип (- файл, d директорія, l символьне посилання). Далі три трійки rwx: read (читати), write (писати), execute (виконувати). У прикладі власник може все (rwx), група й решта — читати й виконувати (r-x).

    Права зручно задавати вісімковим числом, де r=4, w=2, x=1, а сума — набір на трійку:

    chmod 755 run-tests.sh   # rwx для власника, r-x для інших
    chmod 644 config.json    # rw- для власника, r-- для інших
    chmod +x deploy.sh       # просто додати право на виконання

    Навіщо це QA: коли CI лається Permission denied на скрипті, майже завжди причина — забутий біт x. А коли автотест не може прочитати тестовий файл на стенді — дивіться права й власника, а не код тесту.

    Процеси і сигнали

    Кожна запущена програма — процес зі своїм номером (PID, process id). Тестувальнику це потрібно, щоб знайти завислий сервер, перевірити, чи слухається порт, або прибити застряглий процес перед новим прогоном.

    ps aux              # усі процеси системи
    ps aux | grep node  # відфільтрувати за назвою
    top                 # процеси в реальному часі (вихід — q)
    kill 4821           # ввічливо попросити процес 4821 завершитися
    kill -9 4821        # вбити примусово

    kill насправді не «вбиває», а надсилає сигнал. За замовчуванням це SIGTERM (номер 15) — ввічливе «заверши роботу», яке процес може перехопити й коректно закритися. kill -9 — це SIGKILL, який операційна система виконує сама: процес не може його ні перехопити, ні проігнорувати, тому дані можуть лишитися недописаними. Ctrl+C у терміналі — це SIGINT (переривання), а Ctrl+Z — призупинення процесу.

    СигналЯк надіслатиЩо означає
    SIGINT (2)Ctrl+CПерервати, дати завершитися чисто
    SIGTERM (15)kill PIDВвічливе прохання завершитися
    SIGKILL (9)kill -9 PIDПримусово, без шансу прибрати за собою

    Практичне правило: спершу SIGTERM, і лише якщо процес не реагує — SIGKILL. Звичка бити все kill -9 маскує те, що застосунок не вміє коректно вимикатися, а це буває справжнім багом.

    Потоки, пайпи та перенаправлення

    Це ідея, яка перетворює набір команд на інструмент. Кожна програма в Unix має три стандартні потоки: вхід (stdin), вивід (stdout) і потік помилок (stderr). За замовчуванням stdout і stderr ідуть на екран, але їх можна перенаправляти й зчіплювати.

    stdout

    stdout

    stderr

    запис

    Команда 1

    Команда 2

    Команда 3

    Екран

    Файл

    stdout

    stdout

    stderr

    запис

    Команда 1

    Команда 2

    Команда 3

    Екран

    Файл

    Пайп (|) бере stdout однієї команди й подає його на stdin наступної — так будують ланцюжки. Перенаправлення пише потік у файл замість екрана.

    grep ERROR app.log | wc -l        # порахувати рядки з ERROR
    cat app.log | grep 500 | tail -5  # останні 5 рядків з кодом 500
    npm test > out.txt 2> err.txt     # stdout у один файл, помилки в інший
    npm test > all.txt 2>&1           # обидва потоки в один файл
    command > /dev/null 2>&1          # викинути весь вивід у «нікуди»

    Синтаксис 2>&1 читається як «спрямуй потік 2 (stderr) туди ж, куди йде потік 1 (stdout)». Він постійно трапляється в CI-логах і скриптах. А /dev/null — це системна «чорна діра»: усе, що туди записано, зникає; так глушать зайвий шум. Оператор > перезаписує файл, >> — дописує в кінець.

    Розуміння потоків одразу пояснює дві типові загадки CI: чому важливе повідомлення «зникло» (воно пішло в stderr, а лог зберігав лише stdout) і чому джоб «зелений», хоча в логах видно помилку (програма написала помилку в stderr, але повернула код виходу 0).

    Робота з логами: tail, grep, less

    Читання логів — щоденний хліб QA. Лог на бойовому стенді може бути на сотні тисяч рядків, тож відкривати його цілком безглуздо — треба вміти дивитися хвіст, шукати за шаблоном і стежити наживо.

    tail -n 100 app.log     # останні 100 рядків
    tail -f app.log         # стежити наживо: нові рядки зʼявляються самі
    less app.log            # інтерактивний перегляд великого файлу

    tail -f (follow) — найкорисніша команда під час відтворення бага: запускаєте стеження за логом, робите дію в застосунку й бачите, що сервер пише в цю мить. Вихід — Ctrl+C.

    У less навігація як у текстовому редакторі: /слово — пошук уперед, n — наступний збіг, G — у кінець файлу, g — на початок, q — вихід. less не тримає весь файл у памʼяті, тому відкриває навіть гігабайтні логи миттєво.

    grep — фільтр за шаблоном, головний інструмент пошуку в логах:

    grep "ERROR" app.log        # рядки зі словом ERROR
    grep -i "timeout" app.log   # без урахування регістру
    grep -n "500" app.log       # з номерами рядків
    grep -v "healthcheck" app.log   # усе, КРІМ рядків з healthcheck
    grep -C 3 "Exception" app.log   # збіг + 3 рядки контексту навколо
    grep -rn "TODO" ./src       # рекурсивно по директорії

    Прапорець -C (context) особливо цінний у логах: сама помилка часто менш інформативна, ніж рядки до й після неї — стек виклику, вхідні дані, наступна дія. А зʼєднавши grep у пайп, отримуєте точкову вибірку: grep "correlation-id: abc123" app.log витягне всі події одного запиту з мільйона рядків — за умови, що застосунок пише correlation id (наскрізний ідентифікатор запиту; докладніше про це — у главі «Логи, моніторинг і спостережуваність для QA»).

    Логи не завжди лежать простим файлом. Служби під systemd читають через journalctl -u назва-служби -f, а логи контейнера — через docker logs -f назва (див. главу «Docker і тестові середовища»). Але граматика та сама: показати хвіст, стежити наживо, відфільтрувати грепом.

    curl: швидка перевірка HTTP з терміналу

    curl — це «браузер без браузера»: він робить HTTP-запит і показує відповідь. Для QA це найшвидший спосіб перевірити, чи живий ендпоінт, який статус повертає API і що саме в тілі відповіді — без Postman і без відкриття застосунку.

    curl https://api.example.com/health          # GET, тіло у stdout
    curl -i https://api.example.com/users/1      # -i: показати й заголовки
    curl -I https://api.example.com/             # -I: лише заголовки (HEAD)
    curl -v https://api.example.com/             # -v: увесь діалог, запит і відповідь

    Для перевірки записів і авторизації додають метод, заголовки й тіло:

    curl -X POST https://api.example.com/login \
      -H "Content-Type: application/json" \
      -d '{"email":"qa@example.com","password":"secret"}'

    Кілька прапорців, які варто знати: -L іде за редиректами, -o file зберігає відповідь у файл, -s прибирає індикатор прогресу (зручно в пайпах і скриптах), -u user:pass додає базову автентифікацію. А -w '%{http_code}' дописує код статусу у вивід — у зв'язці -o /dev/null -s дає рівно один рядок із кодом, ідеально для смоук-перевірки в CI: чи відповідає стенд узагалі.

    curl природно дружить з пайпами. Класична зв'язка для читабельного JSON — прогнати відповідь через jq:

    curl -s https://api.example.com/users | jq '.[] | .email'

    Що саме означають методи, заголовки й коди статусу — тема розділу «Веб і мережі для AQA»; тут curl цінний як інструмент, що дозволяє перевірити гіпотезу за секунди, не піднімаючи важких клієнтів. Коли автотест на API падає, найшвидший спосіб зрозуміти, тест винен чи бекенд, — повторити той самий запит руками через curl і подивитися сиру відповідь.

    ssh і scp: робота з віддаленим стендом

    Тестові стенди зазвичай живуть на віддалених серверах. SSH (Secure Shell) — це зашифрований канал, який дає термінал на чужій машині так, ніби ви сидите за нею.

    ssh qa@staging.example.com          # підключитися (порт 22 за замовчуванням)
    ssh -p 2222 qa@staging.example.com  # якщо порт нестандартний

    Автентифікуватися можна паролем, але на практиці використовують пару ключів: приватний ключ лежить у вас (~/.ssh/id_ed25519), публічний — на сервері. Сервер перевіряє, що ви володієте приватним ключем, і пускає без пароля. Приватний ключ — це секрет рівня пароля: його не комітять у репозиторій і не пересилають (про гігієну секретів — глава «Секрети та конфігурація в CI»).

    Опинившись на стенді через SSH, ви користуєтеся тими самими tail -f, grep, ps — уже на віддаленій машині. Це і є типовий сценарій розслідування: підключитися до стенду, знайти лог служби, стежити за ним наживо, поки відтворюєте баг.

    scp (secure copy) копіює файли тим самим захищеним каналом — в обидва боки:

    scp report.zip qa@staging:/tmp/         # залити файл на стенд
    scp qa@staging:/var/log/app.log ./      # забрати лог зі стенду до себе
    scp -r ./fixtures qa@staging:/data/     # -r: рекурсивно, цілу директорію

    Забрати лог або артефакт зі стенду до себе, щоб спокійно розібрати, — щоденна операція AQA. Ті самі механізми (SSH-ключі, копіювання артефактів) працюють і всередині CI, коли пайплайн деплоїть код на середовище або забирає результати прогону.

    Змінні середовища і PATH

    Змінна середовища (environment variable) — це іменоване значення, яке оболонка передає всім процесам, що вона запускає. Через них конфігурують застосунки й тести, не вшиваючи значення в код: базовий URL стенду, рівень логування, токени доступу.

    echo $HOME              # вивести значення змінної
    export BASE_URL=https://staging.example.com   # задати на поточну сесію
    printenv                # усі змінні середовища

    Ключове для розуміння: змінні успадковуються дочірніми процесами, але не повертаються назад до батьківського. Тому export у скрипті бачать команди всередині нього, а от нова змінна, задана всередині запущеної програми, не «протече» у вашу оболонку. Постійні змінні прописують у файлах на кшталт ~/.bashrc чи ~/.zshrc, а секрети в проєктах зазвичай кладуть у файл .env і підвантажують бібліотекою (dotenv) — сам .env при цьому не комітять.

    У тестовому коді ці змінні читаються з process.env. Саме так автотести переїжджають між середовищами без правок:

    // playwright.config.ts
    export default defineConfig({
      use: {
        baseURL: process.env.BASE_URL ?? 'http://localhost:3000',
      },
    });

    Особлива змінна — PATH. Це список директорій через двокрапку, у яких оболонка шукає програму, коли ви вводите node, npx чи git. Оболонка перебирає директорії зліва направо й запускає перший знайдений збіг.

    Так

    Ні

    Так

    Ні

    Ввели: npx playwright test

    Це вбудована
    команда оболонки?

    Виконати одразу

    Шукати npx у директоріях PATH
    зліва направо

    Знайдено?

    Запустити перший збіг

    command not found

    Так

    Ні

    Так

    Ні

    Ввели: npx playwright test

    Це вбудована
    команда оболонки?

    Виконати одразу

    Шукати npx у директоріях PATH
    зліва направо

    Знайдено?

    Запустити перший збіг

    command not found

    Це пояснює цілий клас помилок «command not found» у CI: програма встановлена, але її директорії немає в PATH, тому оболонка її не бачить. Перевірити, який саме файл запуститься, можна командою which:

    which node      # /usr/local/bin/node — звідки береться node
    echo $PATH      # побачити весь список директорій пошуку

    Ця сама логіка «шукати виконуваний файл у PATH» лежить в основі багатьох CLI-інструментів, з якими стикається QA. Наприклад, adb для роботи з Android-пристроями (тема мобільного розділу) чи клієнт бази psql (розділ про SQL) — обидва треба мати в PATH, інакше термінал відповість command not found, хоча інструмент установлено.

    Типові помилки

    Виглядає як баг застосунку, а насправді — права або власник файлу. CI падає з Permission denied на скрипті або тест не читає файл на стенді. Причина не в коді, а в забутому біті x чи в тому, що файл належить іншому користувачеві. Дивіться ls -l, а не логіку.

    Виглядає як «джоб зелений», а насправді — помилка пішла в stderr. Програма написала помилку в потік stderr й повернула код виходу 0. Пайплайн бачить нульовий код і вважає крок успішним. Перевіряйте не лише текст логів, а й код виходу ($?), і збирайте обидва потоки (2>&1).

    Виглядає як «команда не існує», а насправді — її немає в PATH. command not found частіше означає не відсутність програми, а те, що оболонка не знає, де її шукати. which і echo $PATH розкривають правду за секунду.

    Виглядає як «стенд не відповідає», а насправді — завислий старий процес тримає порт. Новий інстанс не піднімається, бо порт зайнятий попереднім прогоном. ps aux | grep знаходить винуватця, kill звільняє порт.

    Виглядає як «скрипт не бачить токен», а насправді — змінну не експортовано або вона не успадкувалася. Значення задане в одній оболонці, а команда виконується в іншому процесі, куди воно не долетіло. Без export або в новій сесії змінної просто немає.

    Підсумок

    • Термінал для QA — це доступ до трьох речей, яких немає в браузері: логів на стенді, CI-агентів і керування процесами на віддалених машинах.
    • Пайпи (|) і перенаправлення (>, 2>&1) перетворюють окремі команди на інструмент; stdout і stderr — різні потоки, і плутанина між ними породжує «зелені» джоби з помилками всередині.
    • Логи читають хвостом і фільтром: tail -f для стеження наживо, grep для пошуку за шаблоном і correlation id, less для великих файлів.
    • curl дає перевірити HTTP-ендпоінт за секунди й відповісти на питання «тест винен чи бекенд», повторивши запит руками.
    • Права (chmod, ls -l), змінні середовища й PATH — джерело цілого класу помилок CI, які виглядають як баги коду, але лікуються не в коді.

    Що питають на співбесіді

    Термінал на співбесідах QA має середню вагу — глибокого Linux не чекають, але базову грамотність перевіряють майже завжди, особливо на позиціях з ухилом в автоматизацію.

    • «Як подивитися логи застосунку на сервері?» Інтерв'юер хоче почути ssh на стенд, потім tail -f або less для файлу, grep для пошуку за шаблоном чи id запиту. Сильний кандидат згадає, що логи можуть бути у systemd (journalctl) чи в контейнері (docker logs).
    • «Чим kill відрізняється від kill -9 Перевіряють розуміння сигналів: SIGTERM дає процесу завершитися чисто, SIGKILL вбиває примусово й може лишити дані недописаними. Правильний порядок — спершу мʼяко, потім жорстко.
    • «Що таке PATH і чому може бути command not found?» Дивляться, чи розумієте ви, як оболонка знаходить програму. Відповідь: PATH — список директорій пошуку; якщо каталогу програми там немає, вона «не знайдена», хоча встановлена.
    • «Як швидко перевірити, що API живий, без Postman?» Очікують curl: базовий GET, -i для заголовків, -X POST -d для запису, -w '%{http_code}' для смоуку в CI.
    • «Навіщо змінні середовища в тестах?» Хочуть почути про конфігурацію під середовища без правок коду: базовий URL, токени, рівень логування через process.env, і що секрети не комітять.

    На що дивиться інтерв'юер насправді: не на завчені прапорці, а на те, чи вмієте ви самостійно докопатися до причини — підключитися до стенду, знайти лог, відфільтрувати потрібне й відрізнити проблему середовища від бага застосунку.

    Джерела

    • The Linux Command Line — William Shotts, безкоштовна книга-підручник з bash і базового Linux.
    • GNU Bash Reference Manual — офіційна довідка з оболонки: синтаксис, перенаправлення, змінні.
    • curl documentation — офіційна документація й манпейджі curl.
    • OpenSSH Manual — офіційні манпейджі ssh, scp та конфігурації клієнта.

    Ця тема не входить у силабус ISTQB CTFL 4.0 напряму — це інженерний фундамент, який силабус мовчки припускає в модулі про автоматизацію й інструменти тестування. На співбесідах його перевіряють як практичну гігієну, а не як окремий розділ теорії.