Модульное тестирование bash-скриптов
У нас есть система, в которой есть некоторые bash-скрипты, работающие помимо Java-кода. Поскольку мы пытаемся протестировать все, что может быть сломано, и эти bash-скрипты могут сломаться, мы хотим их протестировать.
Проблема в том, что сложно протестировать bash-скрипты.
Есть ли способ или лучшая практика для тестирования скриптов bash? Или мы должны прекратить использование скриптов bash и искать альтернативные решения, которые можно тестировать?
18 ответов
Я получил следующий ответ от дискуссионной группы:
можно импортировать (включить, что угодно) процедуру (функцию, как бы она ни называлась) из внешнего файла. Это ключ к написанию сценария тестирования: вы разбиваете свой сценарий на независимые процедуры, которые затем могут быть импортированы как в ваш работающий сценарий, так и в сценарий тестирования, и тогда ваш рабочий сценарий будет максимально простым.
Этот метод похож на внедрение зависимостей для скриптов и звучит разумно. Желательно избегать сценариев bash и использовать более тестируемый и менее понятный язык.
На самом деле существует инфраструктура модульного тестирования для сценариев оболочки. Я не использовал это сам, но это могло бы стоить проверить.
Подобные вопросы были заданы ранее:
Tash-совместимое Bash-тестирование: Bash Automated Testing System
TAP, Test Anything Protocol, представляет собой простой текстовый интерфейс между модулями тестирования в тестовом жгуте. TAP начал свою жизнь как часть тестового набора для Perl, но теперь имеет реализации на C, C++, Python, PHP, Perl, Java, JavaScript и других.
Я создал shellspec, потому что хотел простой в использовании и полезный инструмент.
Это написано чистым сценарием оболочки POSIX. Он протестировал со многими снарядами больше, чем shunit2. Он имеет мощные функции, чем летучие мыши / ядро летучих мышей.
Например, поддержка вложенного блока, простой макет / заглушка, простой пропуск / ожидание, параметризованные тесты, номер строки подтверждения, выполнение по номеру строки, параллельное выполнение, случайное выполнение, средство форматирования TAP/JUnit, интеграция покрытия и CI, профилировщик и т. Д.,
Смотрите демо на странице проекта.
Никита Соболев написал отличное сообщение в блоге, в котором сравнивает несколько различных сред тестирования bash: Тестирование приложений Bash
Для нетерпеливых: Никита пришел к выводу об использовании Bats, но похоже, что Никита пропустил основной проект Bats, который, как мне кажется, должен был использоваться в будущем, так как оригинальный проект Bats активно не поддерживается с 2013 года.
Я не могу поверить, что никто не говорил о OSHT! Он совместим как с TAP, так и с JUnit, это чистая оболочка (то есть никакие другие языки не задействованы), он тоже работает автономно, и это просто и прямо.
Тестирование выглядит следующим образом (фрагменты взяты со страницы проекта):
#!/bin/bash
. osht.sh
# Optionally, indicate number of tests to safeguard against abnormal exits
PLAN 13
# Comparing stuff
IS $(whoami) != root
var="foobar"
IS "$var" =~ foo
ISNT "$var" == foo
# test(1)-based tests
OK -f /etc/passwd
NOK -w /etc/passwd
# Running stuff
# Check exit code
RUNS true
NRUNS false
# Check stdio/stdout/stderr
RUNS echo -e 'foo\nbar\nbaz'
GREP bar
OGREP bar
NEGREP . # verify empty
# diff output
DIFF <<EOF
foo
bar
baz
EOF
# TODO and SKIP
TODO RUNS false
SKIP test $(uname -s) == Darwin
Простой прогон:
$ bash test.sh
1..13
ok 1 - IS $(whoami) != root
ok 2 - IS "$var" =~ foo
ok 3 - ISNT "$var" == foo
ok 4 - OK -f /etc/passwd
ok 5 - NOK -w /etc/passwd
ok 6 - RUNS true
ok 7 - NRUNS false
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
ok 9 - GREP bar
ok 10 - OGREP bar
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
not ok 13 - TODO RUNS false # TODO Test Know to fail
Последний тест показывает как "не в порядке", но код выхода равен 0, потому что это TODO
, Можно установить и многословно:
$ OSHT_VERBOSE=1 bash test.sh # Or -v
1..13
# dcsobral \!= root
ok 1 - IS $(whoami) != root
# foobar =\~ foo
ok 2 - IS "$var" =~ foo
# \! foobar == foo
ok 3 - ISNT "$var" == foo
# test -f /etc/passwd
ok 4 - OK -f /etc/passwd
# test \! -w /etc/passwd
ok 5 - NOK -w /etc/passwd
# RUNNING: true
# STATUS: 0
# STDIO <<EOM
# EOM
ok 6 - RUNS true
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
ok 7 - NRUNS false
# RUNNING: echo -e foo\\nbar\\nbaz
# STATUS: 0
# STDIO <<EOM
# foo
# bar
# baz
# EOM
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
# grep -q bar
ok 9 - GREP bar
# grep -q bar
ok 10 - OGREP bar
# \! grep -q .
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
not ok 13 - TODO RUNS false # TODO Test Know to fail
Переименуйте его, чтобы использовать .t
расширение и положить его в t
подкаталог, и вы можете использовать prove(1)
(часть Perl), чтобы запустить его:
$ prove
t/test.t .. ok
All tests successful.
Files=1, Tests=13, 0 wallclock secs ( 0.03 usr 0.01 sys + 0.11 cusr 0.16 csys = 0.31 CPU)
Result: PASS
Установлен OSHT_JUNIT
или пройти -j
произвести вывод JUnit. JUnit также можно комбинировать с prove(1)
,
Я использовал эту библиотеку как для тестирования функций, получая их файлы, так и запуская утверждения с IS
/OK
и их негативы, и сценарии с помощью RUN
/NRUN
, Для меня эта структура обеспечивает наибольшую выгоду для наименьших накладных расходов.
Epoxy - это среда тестирования Bash, которую я разработал в основном для тестирования другого программного обеспечения, но я также использую ее для тестирования модулей Bash, включая себя и Carton.
Основными преимуществами являются относительно низкие затраты на кодирование, неограниченное вложение утверждений и гибкий выбор подтверждений для проверки.
Я сделал презентацию, сравнивая ее с BeakerLib - платформой, используемой некоторыми в Red Hat.
Почему вы говорите, что "сложно" тестировать bash-скрипты?
Что не так с тестовыми упаковщиками, такими как:
#!/bin/bash
set -e
errors=0
results=$($script_under_test $args<<ENDTSTDATA
# inputs
# go
# here
#
ENDTSTDATA
)
[ "$?" -ne 0 ] || {
echo "Test returned error code $?" 2>&1
let errors+=1
}
echo "$results" | grep -q $expected1 || {
echo "Test Failed. Expected $expected1"
let errors+=1
}
# and so on, et cetera, ad infinitum, ad nauseum
[ "$errors" -gt 0 ] && {
echo "There were $errors errors found"
exit 1
}
Попробуй assert.sh
source "./assert.sh"
local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!
Надеюсь, поможет!
Попробуйте bashtest. Это простой способ проверить ваши сценарии. Например, у вас есть do-some-work.sh
которые меняют некоторые конфигурационные файлы. Например, добавить новую строку PASSWORD = 'XXXXX'
в конфигурационный файл /etc/my.cfg
,
Вы пишете команды bash построчно, а затем проверяете вывод.
Установка:
pip3 install bashtest
Создание тестов - это просто написание команд bash.
файл test-do-some-work.bashtest
:
# run the script
$ ./do-some-work.sh > /dev/null
# testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg
$ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
YES
Запустите тесты:
bashtest *.bashtest
Вы можете найти несколько примеров здесь и здесь
Мне очень нравится shell2junit, утилита для генерации JUnit-подобного вывода из тестов скриптов Bash. Это полезно, потому что сгенерированный отчет может быть прочитан системами непрерывной интеграции, такими как плагины JUnit для Jenkins и Bamboo.
В то время как shell2junit не предоставляет всеобъемлющей среды сценариев Bash, такой как shunit2, она позволяет вам получать хорошие отчеты о результатах теста.
Я очень рекомендую https://bashunit.typeddevs.com/ .
Это довольно свежая и простая библиотека тестирования bash-скриптов.
- Бесплатная лицензия с открытым исходным кодом
- Простота установки
- Хорошо документированы
- Активное сообщество и заинтересованное владение
Может быть, это можно использовать или способствовать
https://thorsteinssonh.github.io/bash_test_tools/
Предназначен для записи результатов в протокол TAP, который, по моему мнению, хорош для CI и хорош для тех, кому нужна оболочка. Я предполагаю, что некоторые вещи выполняются в средах оболочки, поэтому некоторые могут утверждать, что должны быть проверены в их среде оболочки.
Я испробовал множество решений, представленных здесь, но нашел большинство из них громоздкими и сложными в использовании, поэтому я создал свою собственную небольшую среду тестирования: https://github.com/meonlol/t-bash
Это просто один файл в репо, который вы можете просто запустить напрямую, с базовым набором утверждений стиля JUnit.
Я профессионально использовал его в нескольких внутренних проектах и смог сделать наши bash-скрипты супер стабильными и устойчивыми к регрессии.
Создать тест
mytest.sh
. Он вызывает ваш скрипт с определенными входами.
Создать текстовый файл
expected/basic.stdout
. Он содержит ожидаемый результат теста.
Передайте их в систему контроля версий (если есть).
Запустите тест и перенаправьте вывод в другой файл:
mytest.sh > actual/basic.stdout
Тогда просто воспользуйтесь инструментом текстового сравнения, чтобы увидеть, где отклоняются результаты. Я думаю, ты можешь просто сделать
diff expected/basic.stdout actual/basic.stdout
Возможно, вы захотите взглянуть на bash_unit:
Взгляните на Outthentic, он прост, расширяем многими языками ( Perl, Python, Ruby, Bash на выбор) и кроссплатформенной ( Linux, Windows) средой для тестирования любых приложений командной строки.
Мне трудно оправдать использование bash для больших скриптов, когда у Python есть такие огромные преимущества:
- Try / Except позволяет писать более надежные сценарии с возможностью отмены изменений в случае ошибки.
- Вам не нужно использовать неясный синтаксис, такой как '
if [ x"$foo" = x"$bar"]; then ...
'который подвержен ошибкам. - Простой анализ параметров и аргументов с помощью
getopt
модуль (и есть еще более простой модуль для разбора аргументов, но имя ускользает от меня). - Python позволяет работать со списками / надписями и объектами вместо базовых строк и массивов.
- Доступ к нужным языковым инструментам, таким как регулярные выражения, базы данных (уверен, что вы можете передать все в
mysql
команда в bash, но это не самый лучший способ написания кода). - Не нужно беспокоиться об использовании правильной формы
$*
или же"$*"
или же"$@"
или же$1
или же"$1"
пробелы в именах файлов не проблема, и т. д., и т. д., и т. д.
Сейчас я использую bash только для самых простых скриптов.