返回顶部
首页 > 资讯 > 精选 >如何为Bash脚本写单元测试
  • 832
分享到

如何为Bash脚本写单元测试

2023-06-15 20:06:56 832人浏览 独家记忆
摘要

小编给大家分享一下如何为Bash脚本写单元测试,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么要为 Bash 脚本写单元测试?因为 Bash 脚本通常都是在执

小编给大家分享一下如何为Bash脚本写单元测试,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

为什么要为 Bash 脚本写单元测试?

因为 Bash 脚本通常都是在执行一些与操作系统有关的操作,可能会对运行环境造成一些不可逆的操作,比如修改或者删除文件、升级系统中的软件包等。

所以为了确保 Bash 脚本的安全可靠,在生产环境中部署之前一定需要做好足够的测试以确保其行为符合我们的预期。

如何能够安全可靠的去测试 Bash 脚本呢?有人可能会说我们可以用 Docker  容器。是的,这样做即安全又方便。在容器隔离出来的环境中不用担心脚本会破坏我们的系统,而且也能非常简单的快速重建出一个可用的测试环境。不过呢,请考虑以下的几个常见的场景:

  • 场景一:在执行 Bash 脚本测试前,我们需要需要事先安装好所有在 Bash  脚本中会用到的第三方工具,否则这些测试将会因为命令找不到而执行失败。例如,我们在脚本中使用了 Bazel 这个构建工具。我们必须提前安装并配置好  Bazel,而且不要忘记为了能够正常使用 Bazel 还得需要一个支持使用 Bazel 构建的工程。

  • 场景二:测试结果的稳定性可能取决于脚本中访问的第三方服务的稳定性。比如,我们在脚本中使用 curl  命令从一个网络服务中获取数据,但这个服务有时候可能会访问失败。有可能是因为网络不稳定导致的,也可能是因为这个服务本身不稳定。再或者如果我们需要第三方服务返回不同的数据以便测试脚本的不同分支逻辑,但我们可能很难去修改这个第三方服务的数据。

  • 场景三:Bash 脚本的测试用例的执行时间取决于脚本中使用的命令的执行时间。例如,如果我们中脚本中使用了 Gradle 来构建一个工程,由于不同的工程大小  Gradle  的一个构建可能要执行3分钟或者3个小时。这还只是一个测试用例,如果我们还有20个或者100个测试用例呢?我们是否还能在几秒内获得测试报告呢?

即使使用了容器来执行 Bash  脚本测试,也一样无法避免上面的几个问题。环境的准备过程可能会随着测试用例的增多而变的繁琐,测试用例的稳定性和执行时长取决于第三方命令和服务的稳定性和执行时长,还可能很难做到使用不同数据来覆盖不同的测试场景。

对于测试  Bash 脚本来说,我们真正要验证的是 Bash 脚本的执行逻辑。比如在 Bash  脚本中可能会根据传入的参数来组合出内部所调用的命令的选项和参数,我们要验证的是这些选项和参数确实如我们预期的。至于调用的命令在接受了这些选项和参数后由于什么原因而失败,可能我们并不关心这所有的可能原因。

因为这会有更多的外部影响因素,比如硬件和网络都是否工作正常、第三方服务是否正常运行、构建工程所需的编译器是否安装并配置妥当、授权和认证信息是否都有效、等等。但对于  Bash 脚本来说,这些外部原因导致的结果就是所调用的命令执行成功或者失败了。所以 Bash  脚本只要关注的是脚本中调用的命令是否能够成功执行,以及命令输出了哪些,并决定随后执行脚本中的哪些不同分支逻辑。

如果说我们就是想知道这个命令搭配上这些选项参数是否能按我们预期的那样工作呢?很简单,那就单独在命令行里面去执行一下。如果在命令行中也不能按预期的工作,放到  Bash 脚本里面也一样不会按预期的工作。这种错误和 Bash 脚本几乎没什么关系了。所以,为了尽量去除影响 Bash 脚本验证的那些外部因素,我们应该考虑为  Bash 脚本编写单元测试,以关注在 Bash 脚本的执行逻辑上。

什么样的测试才是 Bash 脚本的单元测试?

首先,所有存在于 PATH 环境变量的路径中的命令都不应该在单元测试中被执行。对 Bash  脚本来说,被调用的这些命令可以正常运行,有返回值,有输出。但脚本中调用的这些命令都是被模拟出来的,用于模拟对应的真实命令的行为。这样,我们在 Bash  脚本的单元测试中就避免了很大一部分的外部依赖,而且测试的执行速度也不会受到真实命令的影响了。其次,每个单元测试用例之间都应该是独立的。这意味着,这些测试用例可以独立执行或者被任意乱序执行,而不会影响验证结果。最后,这些测试用例可以在不同的操作系统上执行,且都应该得到相同的验证结果。比如  Bash 脚本中使用了只有 GNU/linux 上才有的命令,对应的单元测试也可以在 windows 或者 MacOS 上执行,且结果一致。

怎样为 Bash 脚本写单元测试?

与其他编程语言一样,Bash 也有多个测试框架,比如 Bats、Shunit2 等,但这些框架实际上并不能隔离所有 PATH 环境变量中的命令。有一个名为  Bach Testing Framework 的测试框架是目前唯一一个可以为 Bash 脚本编写真正的单元测试的框架。Bach Testing  Framework 的最独特的特性就是默认不会执行任何位于 PATH 环境变量中的命令,因此 Bach Testing Framework 非常适用于验证  Bash 脚本的执行逻辑。并且还带来了以下好处:

  • 简单

什么也不用安装。我们就可以执行这些测试。比如可以在一个全新的环境中执行一个调用了大量第三方命令的 Bash 脚本。

因为所有的命令都不会被真正执行,所以每一个测试用例的执行都非常快。

  • 安全

因为不会执行任何外部的命令,所以即使因为 Bash 脚本中的某些错误导致执行了一个危险的命令,比如 rm -rf *。Bach  会保证这些危险命令不会被执行。

  • 与运行环境无关

可以在 Windows 上去执行只能工作在 GNU/Linux 上的脚本的测试。

由于操作系统和 Bash 的一些限制,Bach Testing Framework 无法做到:

  • 拦截使用绝对路径调用的命令

事实上我们应该避免在 Bash 脚本中使用绝对路径,如果不可避免的要使用,我们可以把这个绝对路径抽取为一个变量,或者放入到一个函数中,然后用 @mock  api 去模拟这个函数。

  • 拦截诸如 >、>>、<< 等等这样的 I/O 重定向

是的,无法拦截 I/O 重定向。我们也同样可以把这些重定向操作隔离到一个函数中,然后再模拟这个函数。

Bach Testing Framework 的使用

Bach Testing Framework 需要 Bash v4.3 或更高版本。在 GNU/Linux 上还需要 Coreutils 和  Diffutils,在常用的发行版中都已经默认安装好了。Bach 在 Linux/macOS/Cygwin/git Bash/FreeBSD  等操作系统或者运行环境中验证通过。

  • Bash v4.3+

  • Coreutils (GNU/Linux)

  • Diffutils (GNU/Linux)

安装 Bach Testing Framework

Bach Testing Framework 的安装很简单,只需要下载  https://GitHub.com/bach-sh/bach/raw/master/bach.sh 到你的项目中,在测试脚本中用 source 命令导入  Bach Testing Framework 的 bach.sh 即可。

比如:

source path/to/bach.sh

一个简单的例子

与其它的测试框架不同,Bach Testing Framework 的每一个测试用例都是由两个 Bash 函数组成,一个是以test-  开头的测试执行函数,另一个是同名的以-assert 结尾的测试验证函数。比如在下面的例子中,有两个测试用例,分别是

&ndash; test-rm-rf  &ndash; test-rm-your-dot-git

一个完整的测试用例:

#!/usr/bin/env bash set -euo pipefail  source bach.sh # 导入 Bach Testing Framework  test-rm-rf() {     # Bach 的标准测试用例是由两个方法组成     #   - test-rm-rf     #   - test-rm-rf-assert     # 这个方法 `test-rm-rf` 是测试用例的执行      project_log_path=/tmp/project/logs     sudo rm -rf "$project_log_ptah/" # 注意,这里有个笔误!} test-rm-rf-assert() {     # 这个方法 `test-rm-rf-assert` 是测试用例的验证     sudo rm -rf /   # 这就是真实的将会执行的命令                     # 不要慌!使用 Bach 测试框架不会让这个命令真的执行!}  test-rm-your-dot-git() {     # 模拟 `find` 命令来查找你的主目录下的所有 `.git` 目录,假设会找到两个目录      @mock find ~ -type d -name .git === @stdout ~/src/your-awesome-project/.git \                                                 ~/src/code/.git      # 开始执行!删除你的主目录下的所有 `.git` 目录!find ~ -type d -name .git | xargs -- rm -rf } test-rm-your-dot-git-assert() {     # 验证在 `test-rm-your-dot-git` 这个测试执行方法中最终是否会执行以下这个命令。rm -rf ~/src/your-awesome-project/.git ~/src/code/.git }

Bach 会分别运行每一个测试用例的两个方法,去验证两个方法中执行的命令及其参数是否是一致的。

比如,第一个方法 test-rm-rf 是 Bach  的测试用例的执行,与之对应的测试验证方法就是 test-rm-rf-assert这个方法。

在第二个测试用例 test-rm-your-dot-git中使用了  @mockAPI 来模拟了命令find ~ type d -name .git的行为,这个命令用来找出用户目录下的所有 .git  目录。模拟之后,这个命令并不会真的执行,而是利用了 @stdout API 在标准终端上输出了两个虚拟的目录名。

然后我们就可以执行真正的命令了,将  find命令的输出结果传递给xargs 命令,并组合到 rm -rf 命令之后。

在对应的测试验证函数 test-rm-your-dot-git-assert  里面就验证是 find ~ -type d -name .git | xargs -- rm -rf的运行结果是否等同于命令rm -rf  ~/src/your-awesome-project/.git~/src/code/.git

@mock 是 Bach Testing Framework  中很重要的一个 API,利用这个 API 我们就可以模拟 Bash 脚本中所使用的任意命令的行为或者输出。

比如

@mock curl --silent Google.com === \     @stdout "baidu.com"

模拟了命令 curl --silent google.com的执行结果是输出  baidu.com。在真实的正常场景下,我们是无法做到访问google.com得到的是 baidu.com。这样模拟之后就可以用来验证 Bash  脚本中处理一个命令不同响应时的行为了。@mockAPI 甚至还支持更复杂的行为模拟,我们可以自定义一个复杂的模拟逻辑,比如:

@mock ls <<\CMD     if [[ "$var" -eq 1 ]]; then         @stdout one     else         @stdout others     fi CMD

在这个模拟中,会根据变量$var的值来决定命令ls的输出one还是 others。用 @mock API  模拟的命令在任何时候执行的时候都是同样的行为。但如果要模拟同一个命令重复执行的时候要返回不同的值,Bach Testing Framework 还提供了一个  @@mock 这个 API,比如:

@@mock uuid === @stdout aaaa-1111-2222 @@mock uuid === @stdout bbbb-3333-4444 @@mock uuid === @stdout cccc-5555-6666

这三个模拟命令模拟了 uuid  在重复执行三次的时候都返回不同的结果,按照模拟的先后顺序分别输出对应的模拟输出。如果在执行完所有的模拟输出后,再重复执行将会始终输出最后一个模拟的输出。更详细的  API 介绍请在 Bach Testing Framework 的官网 Https://bach.sh 查看。使用 Bach Testing Framework  还可以让我们更安全方便的练习 Bash 编程

比如,我们希望实现一个函数 cleanup 用来删除参数上指定的文件。一个实现可能是:

function cleanup() {     rm $1 }

这个函数的实现其实是有安全问题的,因为对于 Bash 来说,有没有把一个变量用双引号包含起来是非常重要的。在这个实现中,变量 $1  就没有用双引号,这会带来严重的后果。下面我们将使用 @touch API 来创建几个文件,其中将有一个文件名中含有特殊字符的文件bar。

我们都知道,对于含有特殊字符的文件名是要放入到双引号中的。现在这个这个 cleanup  的实现里面没有使用双引号,但是传参的时候使用了双引号,那是否还会按照我们的预期来执行呢?

function cleanup() {     rm -rf $1 }  test-learn-bash-no-double-quote-star() {     # 创建了三个文件,其中有一个名为 "bar*" 的文件     @touch bar1 bar2 bar3 "bar*"      # 要删除这个错误的文件名 bar*,而不删除其他文件,使用了双引号来传参,这是正确的     cleanup "bar*" }  test-learn-bash-no-double-quote-star-assert() {     rm -rf "bar*" }

这个测试用例将会失败,从验证结果中我们可以看到,期望只删除文件 bar,但是在函数 cleanup  里面,因为遗漏了双引号,会导致变量被二次展开。实际执行的命令是 rm -rf "bar*" bar1 bar2 bar3。现在修复函数 cleanup,把变量  $1 放入双引号:

function cleanup() {     rm -rf "$1" }

再次执行测试,会发现确实执行的是命令 rm -rf "bar*"。

Bach Testing Framework 目前已经在宝马集团和华为内部使用了。在宝马集团的一个有数千人规模的大型项目里,Bach Testing  Framework 保证了数个非常重要的构建脚本的维护。

这些脚本的可靠性和稳定性决定了数千人团队的工作效率,现在就可以在本地快速验证这些构建脚本的执行逻辑,也避免了在本地很难复现一些构建集群中的特殊场景的问题。

以上是“如何为Bash脚本写单元测试”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: 如何为Bash脚本写单元测试

本文链接: https://lsjlt.com/news/281579.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何为Bash脚本写单元测试
    小编给大家分享一下如何为Bash脚本写单元测试,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!为什么要为 Bash 脚本写单元测试因为 Bash 脚本通常都是在执行...
    99+
    2023-06-15
  • 如何利用 BATS 测试 Bash 脚本和库
    本篇内容介绍了“如何利用 BATS 测试 Bash 脚本和库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Bash 自动测试系统可以使 Ba...
    99+
    2023-06-15
  • 如何为 Go 函数编写全面的单元测试
    在 go 中编写单元测试有助于确保代码质量和可靠性。单元测试包含导入依赖、设置对象、定义输入和输出、调用函数和断言输出等步骤。通过使用 testing 包中的断言函数,您可以比较实际输出...
    99+
    2024-05-02
    单元测试 go函数 golang
  • java抽象类如何写单元测试
    在Java中,可以使用JUnit框架来编写单元测试。对于抽象类,可以编写一个具体的子类来测试抽象类的方法。以下是一个示例:1. 创建...
    99+
    2023-08-31
    java
  • 为什么要写Bash脚本
    这篇文章主要介绍为什么要写Bash脚本,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!为什么要写Bash脚本其实关于Bash脚本最好的解释如下:The opposite of "it's like r...
    99+
    2023-06-09
  • PYTHON测试脚本怎么写
    PYTHON测试脚本怎么写,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。#!/usr/bin/python# -*- coding: UTF-8 -*-#CHECK ORA...
    99+
    2023-06-04
  • 如何为maven单元测试设置代理
    这篇文章将为大家详细讲解有关如何为maven单元测试设置代理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。背景环境需要设置代理才能够访问外部网络,如果只是运行java程序来访问网络,我们可以...
    99+
    2023-05-31
    maven mave
  • 探讨如何在Vue3中编写单元测试
    当今前端开发中,Vue.js 已经成为了一个非常流行的框架。随着 Vue.js 的不断发展,单元测试变得越来越重要。今天,我们将探讨如何在 Vue.js 3 中编写单元测试,并提供一些最佳实践和常见的问题及解决方案。一、为什么要进行单元测试...
    99+
    2023-05-14
    前端 JavaScript Vue.js
  • 如何编写dota2服务器ping测试脚本
    这篇文章主要介绍“如何编写dota2服务器ping测试脚本”,在日常操作中,相信很多人在如何编写dota2服务器ping测试脚本问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何编写dota2服务器ping测...
    99+
    2023-06-08
  • django写单元测试的方法
       从网上找了很多django单元测试的案例,感觉不是很好用,于是自己写了一套测试方法,在测试环境我们只需要传uri 、请求方式、参数即可一键对所有接口进行...
    99+
    2024-04-02
  • 使用VisualStudio编写单元测试
    一、什么是单元测试 单元测试就是编写一段代码,用来检查某个特定条件下,另外一段代码的行为是否符合我们的预期。单元测试的代码与实际程序的代码具有同等的重要性。每一个单元测试,都是用来定...
    99+
    2024-04-02
  • eclipse单元测试如何弄
    Eclipse提供了一些工具和插件来帮助进行单元测试。以下是在Eclipse中进行单元测试的一般步骤:1. 创建测试类:在Eclip...
    99+
    2023-08-17
    eclipse
  • 在 golang 中为泛型函数编写单元测试
    php小编香蕉为您带来了一篇关于在Golang中为泛型函数编写单元测试的文章。Golang是一种强类型的编程语言,然而,它在泛型方面的支持却相对较弱。因此,为泛型函数编写单元测试可能会...
    99+
    2024-02-11
  • 如何在JavaWeb中编写一个单元测试类测试数据库
    本篇文章给大家分享的是有关如何在JavaWeb中编写一个单元测试类测试数据库,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Java有哪些集合类Java中的集合主要分为四类:1、...
    99+
    2023-06-14
  • 详解如何用JavaScript编写一个单元测试
    目录为什么要进行单元测试?范围界定和编写单元测试保持单元测试简短而简单考虑正面和负面的测试用例分解长而复杂的函数避免网络和数据库连接如何编写单元测试创建一个新项目实现一个类配置和添加...
    99+
    2022-11-13
    JavaScript编写单元测试 JavaScript单元测试
  • 如何使用PHP编写HTTP负载测试脚本?
    在今天的互联网时代,网站的性能越来越重要。对于一个成功的网站来说,它必须能够承受高并发的访问流量,而在实际部署之前,我们需要对网站进行负载测试,以确保网站能够在高流量下正常工作。在本文中,我们将介绍如何使用PHP编写HTTP负载测试脚本来...
    99+
    2023-09-30
    http load shell
  • PHP 单元测试:如何提高测试效率?
    通过使用快速断言、提高代码覆盖率和采用并行测试框架 paratest,可以显着优化 php 单元测试性能,从而将一个需要 30 秒执行的 laravel 测试套件优化到仅需 3 秒,性能...
    99+
    2024-05-21
    php 单元测试 laravel
  • Android下编写单元测试步骤
      1.编写测试类 extends AndroidTestCase   2.编写测试的方法 修饰符是public 直接抛出异常给测试框架 throws Exception...
    99+
    2022-06-06
    单元 单元测试 测试 Android
  • nodejs 单元测试 要写些什么
    Node.js是现代的服务器端JavaScript环境,逐渐成为了许多企业和个人开发项目的首选。在实际开发过程中,单元测试是一项非常重要的部分。通过单元测试,可以保证代码的质量和可靠性,减少开发过程中的错误和调试时间。Node.js的单元测...
    99+
    2023-05-18
  • 使用模拟编写单元测试
    本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《使用模拟编写单元测试》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~问题内容我在里面写了一个rest a...
    99+
    2024-04-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作