Скрипты bash для модульного тестирования

112

У нас есть система, в которой кроме кода Java работают несколько сценариев bash. Поскольку мы пытаемся протестировать все, что может сломаться, а эти сценарии bash могут сломаться, мы хотим их протестировать.

Проблема в том, что скрипты bash сложно тестировать.

Есть ли способ или лучшая практика для тестирования сценариев bash? Или нам следует отказаться от использования сценариев bash и искать альтернативные решения, которые можно протестировать?

nimcap
источник
см. также: stackoverflow.com/questions/1315624/…
Чен Леви
возможный дубликат модульного тестирования для сценариев оболочки
пользователь
Обзор существующих инструментов: medium.com/wemake-services/...
sobolevn

Ответы:

48

На самом деле существует shunit2 , фреймворк модульного тестирования на основе xUnit для сценариев оболочки на основе Bourne. Сам не использовал, но, возможно, стоит проверить.

Подобные вопросы задавались и раньше:

ire_and_curses
источник
3
Я могу утверждать (каламбур), что shunit2 (версия 2.1.6) на сегодняшний день немного сломан. AssertNull и assertNotNull не работают, даже если вы скармливаете им прямые значения. assertEquals работает нормально, но я думаю, что пока мне придется откатить свой собственный.
лабиринт
@labyrinth, вы уверены, что проблема не в этом: github.com/kward/shunit2/issues/53 «Как правильно использовать assertNull?»?
Виктор Сергиенко
1
@Victor Определенно возможно, что я недостаточно осторожен со своими двойными кавычками. Вскоре я вернусь к роли, в которой shunit2 или другая система модульного тестирования bash будут очень полезны. Я попробую еще раз.
лабиринт
5
Я пользователь, а иногда и участник shunit2, и могу подтвердить, что проект жив и здоров в 2019 году.
Алекс Харви,
31

Я получил следующий ответ от дискуссионной группы:

можно импортировать (включать, что угодно) процедуру (функцию, как бы она ни называлась) из внешнего файла. Это ключ к написанию сценария тестирования: вы разбиваете сценарий на независимые процедуры, которые затем можно импортировать как в текущий сценарий, так и в сценарий тестирования, и тогда ваш рабочий сценарий должен быть максимально простым.

Этот метод похож на внедрение зависимостей для скриптов и звучит разумно. Предпочтительно избегать сценариев bash и использовать более проверенный и менее непонятный язык.

nimcap
источник
4
Я не уверен, должен ли я голосовать за или против, с одной стороны, разделение на более мелкие части - это хорошо, но с другой стороны мне нужен фреймворк, а не набор пользовательских скриптов
mpapis
10
Хотя в bash нет ничего плохого (я написал много-много скриптов), это сложный для освоения язык. Мое эмпирическое правило: если размер сценария достаточно велик, чтобы требовать тестирования, вам, вероятно, следует перейти к языку сценариев, который легко проверяется.
Дуг
1
Но иногда вам нужно что-то, что можно получить из пользовательской оболочки. Мне непонятно, как бы вы это сделали, не прибегая к сценарию оболочки
Итковиан
@Itkovian - вы могли бы, например, использовать npm для экспорта исполняемого файла в путь, поэтому не нужно искать источник (ваш пакет npm должен быть установлен глобально)
Элиран Малка
1
Я собираюсь следовать совету не использовать bash. :)
Maciej Wawrzyńczuk
30

TAP- совместимое тестирование Bash : автоматизированная система тестирования Bash

TAP, Test Anything Protocol, представляет собой простой текстовый интерфейс между модулями тестирования в тестовой системе. TAP начал свою жизнь как часть системы тестирования для Perl, но теперь имеет реализации на C, C ++, Python, PHP, Perl, Java, JavaScript и других.

Янус Троельсен
источник
14
Стоит раскрыть, что такое TAP и почему это должно волновать, иначе это просто бессмысленная копипаста
om-nom-nom
@ om-nom-nom: Я связал его с сайтом TAP.
Янус Троелсен
7
Поскольку никто больше не говорил невыразимого: TAP = Test Anything Protocol
JW.
9

Никита Соболев написал отличное сообщение в блоге, в котором сравнивает несколько различных фреймворков для тестирования bash: Тестирование приложений Bash.

Для нетерпеливых: Никита решил использовать Bats, но похоже, что Никита пропустил проект Bats-core, который, как мне кажется, будет использоваться в будущем, поскольку исходный проект Bats не поддерживался активно с 2013 года.

cb2
источник
7

Epoxy - это среда тестирования Bash, которую я разработал в основном для тестирования другого программного обеспечения, но я также использую ее для тестирования модулей bash, включая саму себя и Carton .

Основными преимуществами являются относительно низкие накладные расходы на кодирование, неограниченное вложение утверждений и гибкий выбор утверждений для проверки.

Я сделал презентацию, сравнив ее с BeakerLib - фреймворком, который используют некоторые в Red Hat.

spbnick
источник
6

Почему вы говорите, что «сложно» тестировать сценарии 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
 }
Джим Деннис
источник
4
Во-первых, сценарии bash не очень читабельны. Во-вторых, ожидания сложны, как и проверка того, создан ли файл блокировки с PID сценария bash, который его создал.
nimcap
10
Что еще более важно, сложно тестировать сценарии оболочки, потому что они обычно имеют большое количество побочных эффектов и используют системные ресурсы, такие как файловая система, сеть и т. Д. В идеале модульные тесты не содержат побочных эффектов и не зависят от системных ресурсов.
jayhendren
4

Я создал shellspec, потому что мне нужен был простой в использовании и полезный инструмент.

Он написан чистым сценарием оболочки POSIX. Он протестировал со многими снарядами больше, чем shunit2. Он имеет более мощные функции, чем bats / bats-core.

Например, поддержка вложенного блока, легкость имитации / заглушки, легкость пропуска / ожидания, параметризованные тесты, номер строки утверждения, выполнение по номеру строки, параллельное выполнение, случайное выполнение, форматирование TAP / JUnit, интеграция покрытия и CI, профилировщик и т. Д. .

Смотрите демонстрацию на странице проекта.

Коичи Накашима
источник
3

Мне очень нравится shell2junit , утилита для генерации JUnit-подобного вывода из тестов сценария Bash. Это полезно, потому что созданный отчет затем может быть прочитан системами непрерывной интеграции, такими как подключаемые модули JUnit для Jenkins и Bamboo.

Хотя shell2junit не предоставляет всеобъемлющую среду сценариев Bash, такую ​​как shunit2 , она позволяет вам получать хорошие отчеты о результатах тестирования.

Стив ХХХ
источник
3

Попробуйте баштест . Это простой способ проверить ваши скрипты. Например, у вас есть 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

Вы можете найти несколько примеров здесь и здесь

пахаз
источник
3

Может быть, это можно использовать или внести свой вклад в

https://thorsteinssonh.github.io/bash_test_tools/

Предназначен для записи результатов в протоколе TAP, который, как я полагаю, хорош для CI и хорош для тех, кому нужна среда оболочки. Я полагаю, что некоторые вещи работают в среде оболочки, поэтому некоторые могут поспорить, что ее следует тестировать в среде оболочки.

Hrob
источник
3

Попробуйте assert.sh

source "./assert.sh"

local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!

Надеюсь, поможет!

отметка
источник
3

Не могу поверить, что никто не говорил об 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. Для меня этот фреймворк дает наибольший выигрыш при наименьших накладных расходах.

Дэниел С. Собрал
источник
1

Я пробовал множество решений, представленных здесь, но обнаружил, что большинство из них громоздкие и трудные в использовании, поэтому я создал свою небольшую среду тестирования: https://github.com/meonlol/t-bash

Это всего лишь один файл в репозитории, который вы можете просто запустить напрямую с базовым набором утверждений стиля JUnit.

Я профессионально использовал его в нескольких внутренних проектах и ​​смог сделать наши сценарии bash сверхстабильными и устойчивыми к регрессу.

leondepeon
источник
0

Взгляните на Outthentic , это простой, расширяемый многими языками (Perl, Python, Ruby, Bash по выбору) и кросс-платформенный (Linux, Windows) фреймворк для тестирования любых приложений командной строки.

Алексей Мележик
источник
-2

Мне было трудно оправдать использование bash для более крупных скриптов, когда Python имеет такие огромные преимущества:

  • Try / Except позволяет писать более надежные сценарии с возможностью отмены изменений в случае ошибки.
  • Необязательно использовать непонятный синтаксис, такой как " if [ x"$foo" = x"$bar"]; then ...", который подвержен ошибкам.
  • Простой анализ параметров и аргументов с помощью getoptмодуля (и есть еще более простой модуль для анализа аргументов, но имя ускользает от меня).
  • Python позволяет вам работать со списками / словарями и объектами вместо базовых строк и массивов.
  • Доступ к подходящим языковым инструментам, таким как регулярное выражение, базы данных (конечно, вы можете передать все в mysqlкоманду в bash, но это не самый лучший способ писать код).
  • Не нужно беспокоиться об использовании правильной формы $*или "$*"или "$@"или $1или "$1", пробелы в именах файлов не являются проблемой и т. Д. И т. Д. И т. Д.

Теперь я использую bash только для самых простых скриптов.

слишком много php
источник
3
Не отрицая того факта, что у Python есть преимущества, но ваш второй пункт сформулирован не очень хорошо. Такое же сравнение можно было бы сделать как if [[ $foo = $bar ]]; then .... Это все еще не лучше, чем то, что может предложить python, но лучше, чем то, что вы представили.
Шрикант Шарат
8
Некоторые системы (например, встроенные) не имеют доступного Python, и вы не можете / не хотите устанавливать дополнительные вещи.
Руи Маркес
2
Лично я люблю bash, но согласен, что он может быть немного вспыльчивым. Обычно вы должны быть более активными, тогда как в Python вы можете устранять ошибки после того, как они возникли . Однако в bash есть trap(для очистки / отмены в случае ошибки), а также регулярное выражение (т.е. [[ $1 =~ ^[1-3]{3}$ ]]). Я почти уверен, что использованный вами неясный синтаксис относится к старым реализациям test, а не к bash. Bash отлично подходит для взаимодействия с существующими инструментами командной строки ... Часто один канал для awkили grepнамного проще, чем альтернатива Python.
Шесть
1
Кстати, модуль синтаксического анализатора, о котором вы говорили, вероятно, optparseили его преемник argparse. Я никогда не видел, чтобы кто-то использовал этот getoptмодуль, и не использовал его лично. Однако getoptполезность отличная. Разбор аргумента из оболочки не проблема, если у вас есть хороший шаблон. Если вы не пытаетесь реализовать подкоманды в стиле git или что-то в этом роде, это не составит большого труда.
Шесть
Python не будет работать везде, где доступен bash. Я говорю это, потому что мы тестировали bash и python, с той же логикой кода и просили их что-то сделать. Bash входил во все каталоги, к которым у него был доступ. С другой стороны, python не мог обрабатывать некоторые разрешения каталогов и файлы, а также каталоги, которые увеличивались и уменьшались очень быстро.
vianna77