Bash_unit – bash单元测试框架

2021-03-02 20:13:28

bash_unit允许您编写单元测试(以test开头的功能),运行它们,并在发生故障的情况下显示堆栈跟踪以及源文件和行号指示以定位问题。

在继续阅读本文档之前,您可能希望先了解如何入门。

过滤测试以根据给定的模式运行。您可以通过对每个模式重复此选项来指定多个模式。

bash_unit软件包可通过AUR在Archlinux上使用。为了安装,请发出以下命令:

bash_unit软件包已添加到nixpkgs。您可以通过以下命令使用它:

要运行测试,只需将所有测试文件作为参数调用bash_unit。例如,从bash_unit目录运行一些bash_unit测试:

在运行测试/ test_core.shRunning test_assert_equals_fails_when_not_equal ... SUCCESSRunning test_assert_equals_succeed_when_equal ... SUCCESSRunning test_assert_fails ... SUCCESSRunning test_assert_fails_fails ... SUCCESSRunning test_assert_fails_succeeds ... SUCCESSRunning test_assert_not_equals_fails_when_equal ... SUCCESSRunning test_assert_not_equals_succeeds_when_not_equal ... SUCCESSRunning test_assert_shows_stderr_on_failure ... SUCCESSRunning test_assert_shows_stdout_on_failure测试。 .. SUCCESSRunning test_assert_status_code_fails ... SUCCESSRunning test_assert_status_code_succeeds ... SUCCESSRunning test_assert_succeeds ... SUCCESSRunning test_fail_fails ... SUCCESSRunning test_fail_prints_failure_message ... SUCCESSRunning test_fail_prints_where_is_error ... SUCCESSRunning test_fake_actually_fakes_the_command ... SUCCESSRunning test_fake_can_fake_inline ... SUCCESSRunning test_fake_echo_stdin_when_no_params ... SUCCESSRunning test_fake_exports_faked_in_subshel​​ls。 .. SUCCESSRunning test_fak e_transmits_params_to_fake_code ...成功

您可能还只想运行特定的测试,可以使用-p选项。此选项接受一个模式作为参数,并根据该模式过滤测试函数。

在运行测试/ test_core.shRunning test_assert_equals_fails_when_not_equal ... SUCCESSRunning test_assert_equals_succeed_when_equal ... SUCCESSRunning test_assert_fails ... SUCCESSRunning test_assert_fails_fails ... SUCCESSRunning test_assert_fails_succeeds ... SUCCESSRunning test_assert_not_equals_fails_when_equal ... SUCCESSRunning test_assert_not_equals_succeeds_when_not_equal ... SUCCESSRunning test_assert_shows_stderr_on_failure ... SUCCESSRunning test_assert_shows_stdout_on_failure测试。 .. SUCCESS正在运行test_assert_status_code_fails ... SUCCESS正在运行test_assert_status_code_succeeds ... SUCCESS正在运行test_assert_succeeds ... SUCCESS正在运行test_fail_fails ... SUCCESS

bash_unit支持任何测试协议,因此您可以使用-f选项请求水龙头格式化输出。

#在测试中运行测试/ test_core.shok - test_assert_equals_fails_when_not_equalok - test_assert_equals_succeed_when_equalok - test_assert_failsok - test_assert_fails_failsok - test_assert_fails_succeedsok - test_assert_not_equals_fails_when_equalok - test_assert_not_equals_succeeds_when_not_equalok - test_assert_shows_stderr_on_failureok - test_assert_shows_stdout_on_failureok - test_assert_status_code_failsok - test_assert_status_code_succeedsok - test_assert_succeedsok - test_fail_failsok - test_fail_prints_failure_messageok - test_fail_prints_where_is_errorok - test_fake_actually_fakes_the_commandok - test_fake_can_fake_inlineok - test_fake_echo_stdin_when_no_paramsok - test_fake_exports_faked_in_subshel​​lsok - test_fake_transmits_params_to_fake_code

将测试功能写入文件中。测试功能的名称必须以test开头。仅测试从测试开始的功能。

您可以编写一个设置功能,该功能将在每次测试运行之前执行。

您可以编写一个拆解函数,该函数将在每次测试运行后执行。

您可以编写一个setup_suite函数,该函数将在测试文件的所有测试之前仅执行一次。

您可以编写一个teardown_suite函数,该函数将在测试文件的所有测试之后仅执行一次。

如果您在任何bash函数之外编写代码,则此代码将在测试文件加载时执行一次,因为您的文件是bash脚本,并且bash_unit在运行测试之前先获取它。建议编写一个setup_suite函数,并避免bash函数之外的任何代码。

如果要监视尚未实现的测试,请在函数名前加上todo而不是test。不会执行测试,并且不会影响测试套件的全局状态,而是显示在bash_unit输出中。

bash_unit将当前工作目录更改为正在运行的测试文件之一。如果您需要访问测试代码中的文件(例如,被测脚本),请使用相对于测试文件的路径。

您可能需要更改某些命令的行为,以创建条件以使被测代码的行为符合预期。伪函数可以帮助您实现这一目标,请参见下面的内容。

如果发生故障,将显示标准输出和所评估断言的错误。还显示可选消息。

code(){touch / tmp / the_file} test_code_creates_the_file(){code assert" test -e / tmp / the_file"} test_code_makes_the_file_executable(){code assert" test -x / tmp / the_file&#34 ; " / tmp / the_file应该是可执行的"}

使用assert来检查文件的预期内容也可能很有趣。

code(){echo'不太酷' > / tmp / the_file} test_code_write_appropriate_content_in_the_file(){代码断言" diff<(很酷')/ tmp / the_file"}

如果求值表达式未失败,则assert_fail将失败并显示标准输出和求值断言的错误。还显示可选消息。

code(){echo'不太酷' > / tmp / the_file} test_code_does_not_write_cool_in_the_file(){代码assert_fails" grep cool / tmp / the_file" "不应写' cool'在/ tmp / the_file"}中test_code_does_not_write_this_in_the_file(){代码assert_fails" grep此/ tmp / the_file" "不应写' this'在/ tmp / the_file"}中

如果要在代码中区分几个错误条件,这可能会很有用。

如果发生故障,将显示标准输出和所评估断言的错误。还显示可选消息。

test_obvious_inequality_with_assert_equals(){assert_equals"字符串" "另一个字符串" "字符串应该是另一个字符串"} test_obvious_equality_with_assert_equals(){assert_equals a a}

正在运行test_obvious_equality_with_assert_equals ... SUCCESS正在运行test_obvious_inequality_with_assert_equals ... FAILURE字符串应为另一个字符串,应为[一个字符串],但为[另一个字符串] doc:2:test_obvious_inequality_with_assert_equals()

test_obvious_equality_with_assert_not_equals(){assert_not_equals" string" "字符串" "一个字符串应与另一个字符串不同。"} test_obvious_inequality_with_assert_not_equals(){assert_not_equals a b}

正在运行test_obvious_equality_with_assert_not_equals ... FAILURE一个字符串应该与另一个与[string]期望值不同的字符串不同,但是相同doc:2:test_obvious_equality_with_assert_not_equals()正在运行test_obvious_inequality_with_assert_not_equals ...成功

伪造命令,并在后续测试执行过程中用替换代码(如果指定了代码)替换该命令。如果未指定替换代码,则它将命令替换为一个伪造标准输入的命令。如果您需要为被测代码模拟环境,这可能会很有用。

有人问过是否要使用伪造的结果来创建实际的伪造品,存根或模拟品?还是间谍?还是他们是假人?这个问题的第一个答案是:这要视情况而定。第二个是:阅读有关此子喷头的大量详尽的文献。

这是一个示例,使用stdin参数对假参数进行参数化,以测试某些进程未运行时代码是否失败,否则成功:

code(){ps a | grep apache} test_code_succeeds_if_apache_runs(){false ps<< EOF PID TTY TIME CMD13525 pts / 7 00:00:01 bash24162 pts / 7 00:00:00 ps 8387吗? 0:00 / usr / sbin / apache2 -k startEOF断言代码"代码在apache运行时应该成功"} test_code_fails_if_apache_does_not_run(){false ps<< EOF PID TTY TIME CMD13525 pts / 7 00:00 :01 bash24162 pts / 7 00:00:00 psEOF assert_fails代码"当Apache未运行时,代码应失败"}

如果您需要编写更复杂的代码来伪造您的命令,则可以将此代码抽象为一个函数:

但是请注意,您的_ps函数不会导出到子进程。这意味着,取决于被测代码的工作方式,可能不会在将要调用ps的上下文中定义_ps。例如:

这取决于您的测试代码,但仅导出伪造品所需的功能以使其在子流程中可用则更安全:

伪造也受到以下事实的限制:它定义了一个bash函数以覆盖实际命令。在某些情况下,命令不能被函数覆盖。例如,如果您的被测试代码依赖于exec来启动ps,则伪造不会生效。

当您尝试伪造基本内容时,fake可能还暗示bash_unit的奇怪行为。 bash_unit尝试尽可能避免这种情况,但是有一些限制。尤其令人惊奇的是,bash允许创建以内置命令命名的函数,而bash_unit不会抵抗这种情况。因此,例如,请勿尝试伪造:exit;当地的;陷阱;评估出口;如果;然后;别的; fi;尽管;做;完毕; $;回声; [((我知道,这不是内置的,但不是)。

伪造品将赋予伪造品的参数存储在全局变量FAKE_PARAMS中,以便您可以在伪造品中使用它们。

如果您需要调整给定参数的行为,则可能会很有用。

它也可以帮助声明这些参数的值...但是这可能很棘手。

例如,在我们先前的检查apache的代码中,我们遇到了一个问题,因为我们的代码没有使用带有适当参数的ps。因此,我们将尝试检查提供给ps的参数是否为ax。

code(){ps a | grep apache} test_code_gives_ps_appropriate_parameters(){_ps(){cat<<

此测试将调用代码,该代码将调用ps,该代码实际上是由_ps实现的。由于代码不使用ax而是仅使用a作为参数,因此该测试应失败。但是...

这里的问题是ps失败(由于assert_equals断言失败)。但是ps是通过grep传递的:

使用bash时,管道的结果代码等于管道的最后一条命令的结果代码。最后一个命令是grep,由于grep成功,所以_ps的失败将丢失并且我们的测试成功。我们仅成功地弄乱了测试输出,仅此而已。

一种替代方法是激活bash pipefail选项,但这可能会带来不希望的副作用。我们也可以简单地不在_ps中输出任何内容,以使grep失败:

code(){ps a | grep apache} test_code_gives_ps_appropriate_parameters(){_ps(){assert_equals ax" $ FAKE_PARAMS" }导出-f _ps假ps _ps代码> / dev / null}

这里的问题是,我们使用了一种技巧来使被测代码失败,但是该失败与实际的assert_equals失败无关。这真的很糟糕,请不要这样做。

而且,assert_equals输出由ps捕获,这与我们的测试结果显示混乱了:

唯一正确的选择是让伪造的ps在文件描述符中写入FAKE_PARAMS,以便您的测试可以在代码执行后抓取它们并声明其值。例如,通过写入文件:

code(){ps a | grep apache} test_code_gives_ps_appropriate_parameters(){_ps(){echo $ FAKE_PARAMS> / tmp / fake_params}导出-f _ps假ps _ ps代码|| true assert_equals ax" $(head -n1 / tmp / fake_params)"} setup(){rm -f / tmp / fake_params}

在这里,我们的假货写到/ tmp / fake。我们会在安装程序中删除此文件,以确保不会从以前的测试中获取不正确的数据。我们断言/ tmp / fake的第一行等于ax。另外,请注意,我们知道代码将失败并编写此代码以忽略该错误:code ||真的。

code(){ps a | grep apache} test_code_gives_ps_appropriate_parameters(){假ps&#39echo $ FAKE_PARAMS> / tmp / fake_params' 代码|| true assert_equals ax" $(head -n1 / tmp / fake_params)"} setup(){rm -f / tmp / fake_params} code(){ps a | grep apache} test_get_data_from_fake(){#系紧安全带... coproc cat exec {test_channel}> $ {COPROC [1]}假ps' echo $ FAKE_PARAMS>& $ test_channel' 代码|| true assert_equals ax" $(head -n1<& $ {COPROC [0]})"}