Модульное тестирование для сценариев оболочки
Практически каждый продукт, над которым я работал в течение многих лет, включал в себя некоторый уровень сценариев оболочки (или пакетных файлов, PowerShell и т. Д. В Windows). Несмотря на то, что мы написали основную часть кода на Java или C++, всегда казалось, что существуют задачи по интеграции или установке, которые лучше выполнять с помощью сценария оболочки.
Таким образом, сценарии оболочки становятся частью поставляемого кода и, следовательно, должны быть протестированы так же, как и скомпилированный код. У кого-нибудь есть опыт работы с некоторыми из существующих инфраструктур модульных тестов сценария оболочки, таких как shunit2? Сейчас я в основном заинтересован в сценариях оболочки Linux; Я хотел бы знать, насколько хорошо тестовая система дублирует функциональность и простоту использования других платформ xUnit, и как легко интегрироваться с системами непрерывной сборки, такими как CruiseControl или Hudson.
9 ответов
ОБНОВЛЕНИЕ 2019-03-01: сейчас я предпочитаю летучих мышей. Я использовал его в течение нескольких лет на небольших проектах. Мне нравится чистый, лаконичный синтаксис. Я не интегрировал его со средами CI/CD, но его состояние выхода отражает общий успех / неудачу пакета, который лучше, чем shunit2, как описано ниже.
ПРЕДЫДУЩИЙ ОТВЕТ:
Я использую shunit2 для сценариев оболочки, связанных с веб-приложением Java/Ruby в среде Linux. Он прост в использовании и не сильно отличается от других платформ xUnit.
Я не пробовал интегрироваться с CruiseControl или Hudson/Jenkins, но при реализации непрерывной интеграции с помощью других средств я столкнулся с этими проблемами:
- Состояние выхода: при сбое набора тестов shunit2 не использует ненулевой статус выхода для сообщения о сбое. Таким образом, вы должны либо проанализировать выходные данные shunit2, чтобы определить, прошел / не прошел набор, либо изменить shunit2, чтобы он вел себя так, как ожидают некоторые структуры непрерывной интеграции, сообщая прохождение / сбой через состояние выхода.
- Журналы XML: shunit2 не создает журнал результатов в стиле JUnit XML.
Интересно, почему никто не упомянул летучих мышей. Это современный и TAP- совместимый.
Опишите:
#!/usr/bin/env bats
@test "addition using bc" {
result="$(echo 2+2 | bc)"
[ "$result" -eq 4 ]
}
Бежать:
$ bats addition.bats
✓ addition using bc
1 tests, 0 failures
Roundup by @blake-mizerany звучит великолепно, и я должен использовать его в будущем, но вот мой подход "бедняк" для создания модульных тестов:
- Разделите все тестируемое как функцию.
- Переместите функции во внешний файл, скажем
functions.sh
а такжеsource
это в сценарий. Ты можешь использоватьsource `dirname $0`/functions.sh
для этого. В конце
functions.sh
, вставьте свои тестовые примеры в условие ниже:if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then fi
Ваши тесты - это буквальные вызовы функций, за которыми следуют простые проверки кодов выхода и значений переменных. Мне нравится добавлять простую служебную функцию, подобную приведенной ниже, чтобы ее было легко написать:
function assertEquals() { msg=$1; shift expected=$1; shift actual=$1; shift if [ "$expected" != "$actual" ]; then echo "$msg EXPECTED=$expected ACTUAL=$actual" exit 2 fi }
Наконец, беги
functions.sh
непосредственно для выполнения тестов.
Вот пример, чтобы показать подход:
#!/bin/bash
function adder()
{
return $(($1+$2))
}
(
[[ "${BASH_SOURCE[0]}" == "${0}" ]] || exit 0
function assertEquals()
{
msg=$1; shift
expected=$1; shift
actual=$1; shift
/bin/echo -n "$msg: "
if [ "$expected" != "$actual" ]; then
echo "FAILED: EXPECTED=$expected ACTUAL=$actual"
else
echo PASSED
fi
}
adder 2 3
assertEquals "adding two numbers" 5 $?
)
Сводка новостей: http://bmizerany.github.com/roundup/
В README есть ссылка на статью, подробно объясняющую это.
I recently released new testing framework called shellspec.
shellspec is BDD style testing framework. It's works on POSIX compatible shell script including bash, dash, ksh, busybox etc.
Of course, the exit status reflects the result of running of the specs and it's has TAP-compliant formatter.
The specfile is close to natural language and easy to read, and also it's shell script compatible syntax.
#shellcheck shell=sh
Describe 'sample'
Describe 'calc()'
calc() { echo "$(($*))"; }
It 'calculates the formula'
When call calc 1 + 2
The output should equal 3
End
End
End
В дополнение к roundup и shunit2 мой обзор инструментов для тестирования модулей оболочки также включал assert.sh и http://joyful.com/shelltestrunner/.
Я в основном согласен с критической оценкой автора shunit2 (некоторые из них субъективны), поэтому я исключил shunit2 после просмотра документации и примеров. Хотя, это выглядело знакомо, имея некоторый опыт работы с jUnit.
На мой взгляд, shelltestrunner - самый оригинальный инструмент, на который я смотрел, поскольку он использует простой декларативный синтаксис для определения тестового примера. Как обычно, любой уровень абстракции дает некоторое удобство за счет некоторой гибкости. Несмотря на то, что простота привлекательна, я нашел инструмент слишком ограничивающим для моего случая, в основном из-за отсутствия способа определения действий setup / tearDown (например, манипулировать входными файлами перед тестом, удалять файлы состояний после теста, так далее.).
Сначала я был немного озадачен тем, что assert.sh позволяет утверждать только состояние вывода или выхода, в то время как мне нужны были оба. Достаточно долго, чтобы написать пару тестов с использованием сводки. Но я скоро нашел сводку set -e
режим, неудобный, так как ненулевое состояние выхода в некоторых случаях ожидается как средство передачи результата в дополнение к stdout, что делает тестовый случай неудачным в указанном режиме. Один из примеров показывает решение:
status=$(set +e ; rup roundup-5 >/dev/null ; echo $?)
Но что, если мне нужен как ненулевой статус выхода, так и выход? Я мог бы, конечно, set +e
до вызова и set -e
после или set +e
для всего теста. Но это противоречит принципу обзора "Все является утверждением". Мне казалось, что я начинаю работать против инструмента.
К тому времени я понял, что "недостаток" assert.sh в том, что он позволяет только утверждать либо выход, либо вывод, на самом деле не является проблемой, так как я могу просто передать test
с составным выражением, как это
output=$($tested_script_with_args)
status=$?
expected_output="the expectation"
assert_raises "test \"$output\" = \"$expected_output\" -a $status -eq 2"
Поскольку мои потребности были действительно базовыми (запустить набор тестов, показать, что все прошло нормально или что не получилось), мне понравилась простота assert.sh, поэтому я выбрал именно это.
Вы должны попробовать assert.sh lib, очень удобный, простой в использовании
local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!
Недавно я наткнулся на очень тщательный обзор существующих фреймворков модульного тестирования Bash - https://github.com/dodie/testing-in-bash
Shellspec до сих пор была лучшей, однако это все еще зависит от того, чего вы хотите достичь.
После того, как я искал простую инфраструктуру для модульного тестирования оболочки, которая могла бы генерировать результаты XML для Дженкинса, но ничего не нашел, я написал ее.
Это на sourceforge - проект называется jshu.