Модульное тестирование для сценариев оболочки
почти каждый продукт, над которым я работал на протяжении многих лет, включал некоторый уровень сценариев оболочки (или пакетных файлов, PowerShell и т. д. в Windows.) Несмотря на то, что мы написали большую часть кода на Java или C++, всегда казалось, что некоторые задачи интеграции или установки лучше выполняются с помощью сценария оболочки.
таким образом, скрипты оболочки становятся частью поставляемого кода и поэтому должны быть протестированы так же, как скомпилированный код. Есть ли у кого опыт работы с оболочки сценариев модульных тестов, которые существуют, например shunit2 ? Меня в основном интересуют скрипты оболочки Linux на данный момент; я хотел бы знать, насколько хорошо тестовый жгут дублирует функциональность и простоту использования других фреймворков xUnit и насколько легко интегрироваться с системами непрерывной сборки, такими как CruiseControl или Hudson.
7 ответов:
Я использую shunit2 для сценариев оболочки, связанных с веб-приложением Java/Ruby в среде Linux. Он был прост в использовании и не сильно отличался от других фреймворков xUnit.
Я не пробовал интегрироваться с CruiseControl или Hudson / Jenkins, но при реализации непрерывной интеграции с помощью других средств я столкнулся с этими проблемами:
- состояние выхода: при сбое комплекта тестов shunit2 не использует ненулевое состояние выхода для связи неудача. Таким образом, вам либо нужно проанализировать выход shunit2, чтобы определить проход/сбой набора, либо изменить shunit2, чтобы вести себя так, как ожидают некоторые платформы непрерывной интеграции, передавая pass/fail через состояние выхода.
- XML logs: shunit2 не создает XML-журнал результатов в стиле JUnit.
интересно, почему никто не упомянул летучие мыши. Это до современных и нажми-совместимыми.
описания:
#!/usr/bin/env bats @test "addition using bc" { result="$(echo 2+2 | bc)" [ "$result" -eq 4 ] }Run:
$ bats addition.bats ✓ addition using bc 1 tests, 0 failures
Roundup by @blake-mizerany звучит здорово, и я должен использовать его в будущем, но вот мой "бедный человек" подход для создания модульных тестов:
- отделите все тестируемые функции.
- переместить функции во внешний файл, скажем
functions.shиsourceэто в сценарий. Вы можете использоватьsource `dirname `/functions.shдля этой цели.в конце
functions.sh, встроить тестовые случаи в ниже, если состояние:if [[ "${BASH_SOURCE[0]}" == "" ]]; then fiваши тесты-это литеральные вызовы функций, за которыми следуют простые проверки кодов выхода и значений переменных. Я хотел бы добавить простую функцию полезности, как показано ниже, чтобы сделать его легко писать:
function assertEquals() { msg=; shift expected=; shift actual=; shift if [ "$expected" != "$actual" ]; then echo "$msg EXPECTED=$expected ACTUAL=$actual" exit 2 fi }наконец, запустить
functions.shнепосредственно для выполнения тестов.вот пример, чтобы показать подход:
#!/bin/bash function adder() { return $((+)) } ( [[ "${BASH_SOURCE[0]}" == "" ]] || exit 0 function assertEquals() { msg=; shift expected=; shift actual=; 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, объясняющую это подробно.
кроме Раундап и shunit2 мой обзор инструментов модульного тестирования оболочки также включены assert.sh и shelltestrunner.
Я в основном согласен с критикой автора roundup shunit2 (некоторые из них субъективны), поэтому я исключил shunit2 после просмотра документации и примеров. Хотя, это действительно выглядело знакомым, имея некоторый опыт работы с jUnit.
на мой взгляд shelltestrunner является наиболее оригинал инструментов, которые я рассматривал, поскольку он использует простой декларативный синтаксис для определения тестового случая. Как обычно, любой уровень абстракции дает некоторое удобство за счет некоторой гибкости. Несмотря на то, что простота привлекательна, я нашел инструмент слишком ограниченным для случая, который у меня был, главным образом из-за отсутствия способа определить действия установки/демонтажа (например, манипулировать входными файлами перед тестом, удалять файлы состояния после теста и т. д.).
Я сначала немного запутался что 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 так вот что я выбрал.
после поиска простой модульной тестовой платформы для оболочки, которая могла бы генерировать xml-результаты для Дженкинса и на самом деле ничего не найти, я написал один.
Это на sourceforge-имя проекта jshu.
вы должны попробовать assert.sh lib, очень удобный, простой в использовании
local expected actual expected="Hello" actual="World!" assert_eq "$expected" "$actual" "not equivalent!" # => x Hello == World :: not equivalent!
Comments