本文介绍了在 shUnit2 中重定向文件输出的单元测试 bash 脚本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 shUnit2 在 Bash shell 脚本中进行单元测试.

我有这样的代码:

cat >/etc/somefile <<EOF一些文件内容EOF

我想编写单元测试来测试这段代码,但为此我需要避免文件 I/O 重定向.

我意识到我可以重构代码以移动在函数内部重定向文件输出的位,例如

cat_somefile() {猫 >/etc/somefile <<EOF一些文件内容EOF}

然后我可以在我的测试中存根这个函数.

有没有什么方法可以在不重构的情况下测试这段代码?

解决方案

正如@chepner 所建议的(另见 ref) chroot 可以在这里提供帮助.

要设置这一切:

1/授予 sudo 访问权限以运行 chroot 命令.

$ sudo visudo...%admin ALL = (ALL) NOPASSWD:/usr/sbin/chroot

2/创建一个包含脚本和一个函数来设置监狱.

FAKE_ROOT=./fake_root复制到监狱(){d=$(目录名 $1)mkdir -p $FAKE_ROOT/$dcp -p $1 $FAKE_ROOT/$d}监狱文件(){# 所有这些命令至少被其中一个使用过一次# 正在测试的脚本.命令=/斌/猫/bin/chmod/bin/日期/bin/mkdir/bin/rm/usr/bin/awk/usr/bin/cut/usr/bin/tee"本地 OPTIND 脚本包装器而 getopts "s:w:" o做案例${o}"在s)脚本="${OPTARG}";;w)包装器="${wrappers} ${OPTARG}";;esac完毕移位 $((OPTIND-1))# 没有/bin/sh 监狱就不能工作.# 我没有花时间去弄清楚原因.对于/bin/sh $script $wrappers $commands 中的 f做copy_to_jail $f完毕# 我们找出所有需要构建的库文件# 一个工作监狱只是通过尝试启动它,观察哪个文件# 它说丢失,复制丢失的文件,并重复# 直到它开始.案例 $(uname -s) 在达尔文)# 复制 Bash 4 和 GNU Sed.cp -p/usr/local/bin/bash $FAKE_ROOT/bincp -p/usr/local/bin/gsed "$FAKE_ROOT/bin/sed"initial_libs=$(对于/usr/local/bin/bash 中的 f做otool -L $f |awk 'NR >1 {打印 $1}'完成 |排序 -u)initial_libs="/usr/lib/dyld $initial_libs"对于 $initial_libs 中的 f做copy_to_jail $f完毕who_i_am=$(whoami)set -o 管道故障虽然是真的做missing_lib=$(sudo chroot -u $who_i_am $FAKE_ROOT/bin/sh -c echo 2>&1 | \awk '/dyld: 库未加载:/{print $5}')[$?"-eq 0] &&休息copy_to_jail $missing_lib完毕;;*)echo "不支持平台 $(uname -s)"出口 1;;esac}clean_up_jail() {rm -rf $FAKE_ROOT}

3/在测试文件中:

.shunit2/include.shscript_under_test=path/to/my/script.shmake_jail() {# 复制脚本依赖的任何二进制文件#去监狱.监狱文件\-s $script_under_test# 创建任何预期存在的目录.mkdir -p $FAKE_ROOT/etc/apt/preferences.dmkdir -p $FAKE_ROOT/etc/{default,init.d}mkdir -p $FAKE_ROOT/etc/apache2/sites-enabledmkdir -p $FAKE_ROOT/tmp...# 使用初始内容设置任何文件.echo "一些初始内容" >$FAKE_ROOT/etc/hostswho_i_am=$(whoami)须藤 chroot -u $who_i_am $FAKE_ROOT \/bin/$(basename $script_under_test) >/开发/空}oneTimeSetUp() {make_jail}oneTimeTearDown() {clean_up_jail}# 编写实际测试.test_something() {...}test_something_else() {...}# 调用 shUnit2..shunit2

我会在某个阶段写一篇关于这个的博文.

I am using shUnit2 to do unit testing in Bash shell scripts.

I have code like this:

cat > /etc/somefile <<EOF
some file content
EOF

I want to write unit tests to test this code, but to do that I would need to avoid the file I/O redirection.

I realise I could refactor the code to move the bits that redirect file output inside functions, e.g.

cat_somefile() {
  cat > /etc/somefile <<EOF
some file content
EOF
}

And then I could stub this function in my tests.

Is there any way to test this code without refactoring it though?

解决方案

As suggested by @chepner (and see also ref) chroot can help here.

To set this all up:

1/ Grant sudo access to run the chroot command.

$ sudo visudo
...
%admin    ALL = (ALL) NOPASSWD: /usr/sbin/chroot

2/ Create an include script and a function to set up the jail.

FAKE_ROOT=./fake_root

copy_to_jail() {
    d=$(dirname $1)
    mkdir -p $FAKE_ROOT/$d
    cp -p $1 $FAKE_ROOT/$d
}

jail_files() {

    # All of these commands are used at least once by one of the
    # scripts under test.

    commands="
        /bin/cat
        /bin/chmod
        /bin/date
        /bin/mkdir
        /bin/rm
        /usr/bin/awk
        /usr/bin/cut
        /usr/bin/tee
    "

    local OPTIND script wrappers
    while getopts "s:w:" o
    do
        case "${o}" in
        s)
            script="${OPTARG}"
            ;;
        w)
            wrappers="${wrappers} ${OPTARG}"
            ;;
        esac
    done
    shift $((OPTIND-1))

    # Without /bin/sh the jail doesn't work.
    # I didn't spend time to figure out why.

    for f in /bin/sh $script $wrappers $commands
    do
        copy_to_jail $f
    done

    # We figure out all the library files we need to build
    # a working jail just by trying to start it, observing which file
    # it says is missing, copying the missing file, and repeating
    # until it starts.

    case $(uname -s) in
    Darwin)
        # Copy Bash 4 and GNU Sed.
        cp -p /usr/local/bin/bash $FAKE_ROOT/bin
        cp -p /usr/local/bin/gsed "$FAKE_ROOT/bin/sed"

        initial_libs=$(
            for f in /usr/local/bin/bash
            do
                otool -L $f | awk 'NR > 1 {print $1}'
            done | sort -u)
        initial_libs="/usr/lib/dyld $initial_libs"

        for f in $initial_libs
        do
            copy_to_jail $f
        done

        who_i_am=$(whoami)

        set -o pipefail
        while true
        do
            missing_lib=$(sudo chroot -u $who_i_am $FAKE_ROOT /bin/sh -c echo 2>&1 | \
                awk '/dyld: Library not loaded:/ {print $5}')
            [ "$?" -eq 0 ] && break
            copy_to_jail $missing_lib
        done
        ;;

    *)
        echo "Platform $(uname -s) not supported"
        exit 1
        ;;
    esac
}

clean_up_jail() {
    rm -rf $FAKE_ROOT
}

3/ In the test file:

. shunit2/include.sh

script_under_test=path/to/my/script.sh

make_jail() {

    # Copy any binaries that the script depends on
    # to the jail.

    jail_files \
        -s $script_under_test

    # Create any directories that are expected to exist.
    mkdir -p $FAKE_ROOT/etc/apt/preferences.d
    mkdir -p $FAKE_ROOT/etc/{default,init.d}
    mkdir -p $FAKE_ROOT/etc/apache2/sites-enabled
    mkdir -p $FAKE_ROOT/tmp
    ...

    # Set up any files with initial content.
    echo "some initial content" > $FAKE_ROOT/etc/hosts

    who_i_am=$(whoami)
    sudo chroot -u $who_i_am $FAKE_ROOT \
        /bin/$(basename $script_under_test) > /dev/null
}

oneTimeSetUp() {
    make_jail
}

oneTimeTearDown() {
    clean_up_jail
}

# Write actual tests.
test_something() {
  ...
}

test_something_else() {
  ...
}

# Call shUnit2.
. shunit2

I will write a blog post about this at some stage.

这篇关于在 shUnit2 中重定向文件输出的单元测试 bash 脚本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 06:45