Тестирование и отладка Shell-скриптов#

Тестирование#

Bats#

Bats — это фреймворк для тестирования Bash-скриптов. Bats предоставляет Bash-совместимый синтаксис для написания тестовых скриптов. Имеет не самую плохую документацию. Я не буду здесь её пересказывать, а просто покажу примеры тестов.

Следует отметить, что для нормального экспириенса потребуется в папку с проектом доставить из Git’а как минимум пару субмодулей (helpers) для Bats. В моём случае:

git submodule add https://github.com/bats-core/bats-assert.git tests/helpers/bats-assert
git submodule add https://github.com/bats-core/bats-support.git tests/helpers/bats-support
git commit -m 'Add bats-assert and bats-support libraries'

Хэлперы подключаются в тестовых скриптах в специальной функции setup().

Здесь я тестирую функцию parse_uri() из файла lib/uri.sh. Функция принимает строку c URI и задаёт набор переменных с соответствующими компонентами URI. В тесте я проверяю, что значения этих переменных соответствуют ожидаемым. Обратите внимание на шебанг, тестовый файл также имеет расширение .bats для наглядности.

На 19-й строке я читаю uri.sh и далее вызываю функцию с аргументом. Сопоставление переменых со значениями проверяется обычным test(1)-ом. Тест будет провален, если тестируемый скрипт выйдет с ненулевым кодом.

 1#!/usr/bin/env bats
 2
 3# parse_uri() from lib/uri.sh tests.
 4# See: https://bats-core.readthedocs.io/en/latest/index.html
 5
 6setup() {
 7    # Bats setup
 8    load 'helpers/bats-support/load'
 9    load 'helpers/bats-assert/load'
10    DIR="$( cd "$( dirname "$BATS_TEST_FILENAME" )" >/dev/null 2>&1 && pwd )"
11    PATH="$DIR/../src/lib:$PATH"
12}
13
14# ------------------------------ #
15# Do tests!                      #
16# ------------------------------ #
17
18@test "http://user:password@host:port/path?q=val" {
19    . uri.sh
20    parse_uri 'http://user:password@host:123/path?q=val'
21    [ "$scheme" == 'http' ]
22    [ "$hostname" == 'host' ]
23    [ "$port" == '123' ]
24    [ "$path" == '/path' ]
25    [ "$username" == 'user' ]
26    [ "$password" == 'password' ]
27    [ "$query" == 'q=val' ]
28    [ "$fragment" == '' ]
29}

Вот так можно проверить STDOUT скрипта (я проверяю здесь функцию, но суть не изменится для файла):

1@test "Bad script syntax" {
2    . source.sh
3    __user_script=script.sh
4    run source_script $DIR/files/bad_syntax.sh
5    assert_output --partial 'Error: script.sh: Please check your syntax'
6}

Тест будет провален, если функция source_script() не вернёт текст ошибки если скормить ей заведомо некорректные данные.

Тесты запускаются так:

bats --verbose-run --print-output-on-failure parse_uri.bats

Shpec#

Ещё одна тулза для написания тестов. Пока не потыкал, но выглядит потенциально очень интересно. Возможно будет удобней Bats.

Отладка#

Самописные решения#

BASHDB#

Пока пользоваться не приходилось, но если буду, обязательно здесь опишу.

Линтинг#

Shellckeck#

Shellckeck это крайне полезный инструмент для быстрого улучшения кода. Он покрывает очень много кейсов и помогает закрыть по крайней мере часть потенциальных ошибок в скриптах.

Shellckeck анализирует код на предмет вредных и опасных паттернов и даёт подсказки по стилю кода. На каждое выдаваемое программой замечание есть отдельная статья в Wiki с обоснованием такого решения. Мешающие проверки можно отключить в коде с помощью специально оформленных комментариев.

Утилита есть в репозиториях большинства дистрибутивов. Пользоваться ей очень просто:

shellcheck my_script.sh