Тестирование модулей для сценариев оболочки

Практически каждый продукт, над которым я работал на протяжении многих лет, включал некоторый уровень сценариев оболочки (или пакетных файлов, PowerShell и т.д. в Windows). Несмотря на то, что мы написали основную часть кода на Java или С++, всегда казалось, что есть некоторые задачи интеграции или установки, которые лучше выполнялись с помощью оболочки script.

Скрипты оболочки, таким образом, становятся частью отправленного кода и поэтому должны быть протестированы точно так же, как и скомпилированный код. Кто-нибудь имеет опыт работы с некоторыми оболочками script unit test, которые находятся там, например shunit2? На данный момент меня интересуют только сценарии оболочки Linux; Я хотел бы знать, насколько хорошо тестовая жгута дублирует функциональность и простоту использования других инфраструктур xUnit и насколько легко интегрироваться с системами непрерывной сборки, такими как CruiseControl или Hudson.

Ответ 1

ОБНОВЛЕНИЕ 2019-03-01: сейчас я предпочитаю летучих мышей. Я использовал его в течение нескольких лет на небольших проектах. Мне нравится чистый, лаконичный синтаксис. Я не интегрировал его со средами CI/CD, но его состояние выхода отражает общий успех/неудачу пакета, который лучше, чем shunit2, как описано ниже.


ПРЕДЫДУЩИЙ ОТВЕТ:

Я использую shunit2 для сценариев оболочки, связанных с веб-приложением Java/Ruby в среде Linux. Он был прост в использовании и не сильно отличался от других платформ xUnit.

Я не пробовал интегрироваться с CruiseControl или Hudson/Jenkins, но при реализации непрерывной интеграции с помощью других средств я столкнулся с этими проблемами:

  • Состояние выхода: при сбое набора тестов shunit2 не использует ненулевой статус выхода для сообщения о сбое. Таким образом, вы должны либо проанализировать выходные данные shunit2, чтобы определить, прошел или не прошел набор, либо изменить shunit2 на поведение, ожидаемое некоторыми средами непрерывной интеграции, сообщая прохождение/сбой через состояние выхода.
  • Журналы XML: shunit2 не создает журнал результатов XML в стиле JUnit.

Ответ 2

Интересно, почему никто не упомянул летучих мышей. Это современное и TAP -compliant.

Опишите:

#!/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

Ответ 3

Roundup by @blake-mizerany отлично звучит, и я должен использовать его в будущем, но вот мой подход "плохого человека" для создания модульных тестов:

  • Отделить все, что можно проверить как функцию.
  • Переместите функции во внешний файл, скажем functions.sh и source в script. Вы можете использовать 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 $?
    )

Ответ 5

В дополнение к roundup и shunit2 мой обзор инструментов модульного тестирования оболочки также включал assert.sh и 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, и то, что я выбрал.

Ответ 6

После поиска простой фреймворка unit test для оболочки, который мог бы генерировать xml-результаты для Jenkins и не нашел ничего, я написал один.

Это на sourceforge - имя проекта jshu.

http://sourceforge.net/projects/jshu

Ответ 7

Вы должны попробовать assert.sh lib, очень удобный, простой в использовании

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

Ответ 8

Недавно я выпустил новый фреймворк для тестирования под названием shellspec.

shellspec - это среда тестирования в стиле BDD. Он работает на POSIX-совместимом сценарии оболочки, включая bash, dash, ksh, busybox и т.д.

Конечно, состояние выхода отражает результат выполнения спецификаций и имеет TAP-совместимый форматер.

Specfile близок к естественному языку и прост для чтения, а также поддерживает синтаксис, совместимый со сценарием оболочки.

#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