• 0
  • 0
分享
  • Bug越改越多?是哪里出了问题?——软件测试圈
  • 曼倩诙谐 2023-07-03 13:17:48 字数 4046 阅读 1251 收藏 0

  乔梁在MTSC大会上分享了主题为《一致性是研效提升的必经之路》的演讲,演讲内容主要是如何提高软件研发效率。他从香农在信息论中的熵增定律出发,讲到像软件这样的复杂系统,必须尽可能减少不确定性因素的影响,努力提高软件研发的一致性,从而达到提高软件研发效率的目的。本文主要基于熵增定律,探讨该定律对软件测试行业是否存在指导作用。

  首先,我们回顾一下什么是熵增定律。这原本是热力学中的概念,一个孤立系统总是趋向于熵增,最终达到熵的最大状态,也就是系统的最混乱无序状态。1948年,香农将热力学的熵,引入到信息论。

  对于一个信息系统,如果没有外力作用,最终也会达到信息熵最大的状态,信息系统会越来越混乱,不确定性会越来越强。

  影响软件系统运行的因素非常多,每个因素之间都会相互影响,所以根本无法根据单一因素来预测软件最终的状态。这和股市很相似,你最好永远不要通过某个单一信息预测股市的走势,因为单一因素和最终的结果不是线性相关的。

  Bug 是如何产生的

  一个软件写好了之后,你直接部署好,过了一段时间再去看,它可能就不是你预期的样子了,可能早已经报了网关错误, 可能页面展示 500 错误,或者干脆无法访问了。有很多原因都有可能造成这种混乱的状态,比如某个用户的操作触发了一个隐藏很深的缺陷,对程序造成了一个很小的修改,之后其他用户再进行了一些其他的操作, 也有可能是遭受恶意攻击等, 当然还有可能是你的服务器到期没续费,或者是云服务商倒闭了。

  所以就算你的软件已经接近完美,不再需要任何修改和更新,你可以很长时间不去维护,软件也会变得越来越混乱。而在一个产品的实际研发过程中,我们还需要不停的为软件系统添加新功能,增加新特性,带来更多信息(比特),导致系统更加复杂,这些新信息会进一步增加软件的信息熵。

  一个软件系统越复杂,影响的因素越多,就越难通过人力控制。几乎所有的公司都想利用已经成熟的技术体系提高研发效率,但是实际上研发效率只会越来越低。

  一个产品往往是初创阶段研发效率高,越到后面效率越低。原因之一就是因为业务增长必然导致信息熵增加,不确定性更高,干扰因素更多,从而拖慢研发进程,产生更多 bug。

  在信息论中,信息熵通常用概率的倒数表示。概率越低表示程序的不确定性越高,信息熵越高,随机性事件就更容易形成 bug。

  哪些主要因素会增加系统的不确定性?

  第一种因素是依赖的环境不确定。比如操作系统发生变化、数据库品牌和版本发生变化、访问的文件权限发生变化。

  第二种因素是状态管理。可能发生变化的任何数据都处于某种状态,比如程序中的变量、数据库中存储的数据、文件中的数据。这些数据可能被同一个系统的不同阶段读取和修改,但是外部根本不知道他什么时候修改的,从而造成不确定性。漫游测试中的快递测试法采取跟踪数据,查看系统中哪些特性会读取和修改这个数据,从而测试这种不确定性。

  第三种因素是程序的分支路径。如果你没有一个非常完备的定位系统,当你走在一条满是岔路的主干道上,你一定会迷路。

  对于一个复杂的软件系统来说,熵增几乎是必然的,再加上加入越来越多的新功能(信息熵),软件的质量也会越来越无法保障, 我们能做的,只有投入越来越高的成本,延缓它的混乱状态形成过程。

  我们也可以利用这种混乱状态的形成原因,改善自己的工作,就像你虽然无法预测股市的走势,但照样可以从中赚钱一样。所有的不确定性系统都没有办法通过单一因素进行控制,因为你不知道你控制的因素会不会对其他因素产生影响。所以你应该放弃对不确定性系统的控制,让他自由发展,我们要做的事情就是:计算不确定性,计算概率。

  比如你计算出某只股票上涨的概率有 100%,那就代表世界上没有哪个因素会阻止它下跌,这显然是不可能的。你可以自己列举影响股价走势的因素,然后分配权重,最终计算出概率。

  需要注意的是,你永远不可能列出所有的因素,但你也不用慌,你可以把没有考虑到的其他因素放到一起,叫其他,也分配一个权重。在一次一次的操作中,你会发现你的模型不够好,计算出的概率不精准,当你发现了其他因素,再修改和调整模型。

  不确定性对软件测试的指导意义

  开发软件也好、测试软件也好,和买卖股票并没有什么区别,还是计算不确定性,你需要计算哪些随机事件会对系统产生影响。然后设法提高确定性和一致性。

  现在我们写一个很简单的程序:

def run():
    print("hello world")

  这是一个信息熵很低的程序,确定性高,每次运行这个程序几乎总是得到一个确定的结果:在显示设备上打印 hello world。但你永远不能说它是确定的,发生的概率是 100%。如果程序依赖的环境变化了呢?比如某个新的版本不再支持这个 print 函数了呢?比如 print 的用法发生变化了呢?比如你的操作系统不能正确安装?比如你的显示设备无法正常工作?

  幸运的是通过 DevOps 研发体系,我们可以使用 docker 这样的确定性工具来部署,尽量保证依赖环境的一致性,因此在测试时我们可以减少环境不确定性带来的权重,但是并不表示我们不需要测试环境本身。比如用户选择什么样的客户端、客户端版本都是我们不能确定的,那就不要对不同的浏览器,不同的app版本进行测试。(当然程序也可以选择强制规定用户只能选择某个客户端)

  对于系统、数据库、客户端、版本号、解释器等所有的依赖最好纳入缺陷分析的范围中。

  接下来是用户输入造成的不确定性。任何一个程序,用户的输入是不受控制的,这就会对程序造成非常大的随机性的影响。

def run(user_input):
    print(user_input)

  print 函数能处理所有的用户输入吗?字符串可以?整数可以?emoji 可以?100G的数据也可以?如果程序里面调用了其他程序 do_something(user_input), 这个程序可以处理所有的用户输入吗?

def run(user_input):
    do_something(user_input)

  在 java 这样的静态类型语言上,已经对用户输入的参数类型从语言层面做了类型检测,提供了标准化和一致性的方法, 而像 python 这样的动态类型语言则必须由开发人员自己进行校验,程序的确定性保障更多的需要依靠开发人员的逻辑能力和编码水平,而不同开发人员会形成不同的处理方法,增加了程序的不确定性。

  虽然静态语言对数据类型进行了类型检测,提供了一致性方法,但是不代表研发人员就可以放任用户数据不管。因为用户输入可以通过其他途径,比如输入长度超过处理能力,比如输入恶意数据破坏程序等等。

  总之,开发需要进一步控制用户输入,就会编写分支语句和异常处理语句对程序进行控制,就好像为了方便所有的居民出行,一定会在主干道上挖出千奇百怪的小路一样。还是那句话,分支路径多了,人就容易迷路。

def run(user_input):
    if user_input > 10: ...
    elif user_input > 100: ... 
    if isinstance(userinput, str): ...
    try:    
        do_something(user_input)
    except ValueError: ...

  对于完全无法确定的随机用户数据,程序会编写分支语句来形成确定性的执行,这多个分支是否运行就是概率问题。测试程序时就必须考虑,开发人员是否考虑了所有的情况,如果某个用户输入没有对应的分支执行怎么办?如果开发人员的分支处理不合理怎么办?

  在编写代码的时候,if 语句忘记处理 else,分支覆盖不全的情况比比皆是。异常捕获语句则只考虑了自己能力范围内的异常类型,有大量未知异常没有按预期方式处理。开发人员还会使用大量第三方库进行参数验证,简化自己的参数验证代码,但是这些第三库是否符合要求,有没有漏洞和bug,是否按正确方式使用了,又是个不确定性问题。

  除了用户的输入参数,在程序运行过程中,还会使用其他的数据,比如系统生成的时间戳、随机生成的变量,使用了经由其他程序修改后的数据等等。这些数据都不是程序员自己能控制的,属于随机性事件,因而很难被发现、控制和测试。

  对于开发人员来说,尽量编写确定性高的代码,能有效抑制 bug 的产生。什么是确定性高的代码呢?比如编写纯函数,尽量减少对其他接口的依赖、不随意修改全局变量、不随意修改类中的属性, 尽量减少对文件、数据库等状态数据的依赖。(这样的代码我叫做可测试性高,但是有几家公司是这样写代码的。)

  对于测试人员来说,则需要分析哪些特性适合编写纯函数、哪些地方的数据是系统生成的、分析用户输入会形成哪些分支代码、分析依赖了哪些数据库数据、文件数据、以及其他接口提供的数据,同时,测试人员自己编写的代码也应该参考这些情况,减少不确定性。

  最后,多个程序之间、多个特性之间、多个模块之间相互调用形成的不确定性更加难以控制。功能越复杂,涉及到的数据会越多, 根据不同的操作顺序,形成的排列组合会成指数级上升。使用场景测试法、判定表等方案从理论上是可行的,但是实际测试过程中很难全部覆盖到。目前通过像智能化测试、精准测试这样的技术也许能缓解部分测试难题,但是这些技术手段本身的信息熵就很高。

  乔梁还从管理角度提出了以下一些提高一致性、减少不确定性的解决方案,可以作为参考。

  流程:同一流程,不同人执行的产出结果与质量是否一致?

  工具:同一件任务,内部有多少种工具平台?

  个人:同一产品代码,在多大程度上像是同一个人写出来的?

  组织:在结构与机制上,是否支持员工追求“一致性”?


作者:九柄    

来源:http://www.51testing.com/html/21/n-7792121.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 记录自动化中遇到的一些错误及解决思路,会持续更新(根据个人情况不同,错误解决方法不同)1、找不到元素,脚本报“NoSuchElementException:Unable to find element”,或者"定位到了,不能操作,点击无效"首先查看自己的“属性值”是否写正确;元素的标签不唯一,默认找到第一个;向上查看,元素是否在frame或iframe框架中;查看元素是否在新打开的页面中,需要切换到新窗口;换其它的定位方式:id/name/class name/tag name/link text/xpath/css selector;检查元素属性是否是会变动的、是否是隐藏...
            11 11 4088
            分享
          •   二舅火了,《回村三天,二舅治好了我的精神内耗》在朋友圈疯狂转发,看很多测试同事发圈感慨二舅治好了自己的精神内耗,让自己不再纠结那些得不到的东西。  想必你也经历过一刷激动、二刷感动、三刷四刷平平无奇的感觉,到现在可能你已经想不起来视频讲了什么。曾经短暂平和的内心是不是又泛起焦虑不安的浪花?  二舅治不好你的精神内耗,测试人的精神内耗是二舅无法治愈的痛。  时代变了,每个时代各有自己的烦恼,不尽相同。生活是苦的,二舅生活在那个物质匮乏、信息封闭的年代,活着已经是那个刚刚经历百年动荡后,大部分人追求的目标。  从这个角度看,二舅做得很不错了,他能在少年残疾的情况下活下来,还有一些积蓄和本领,这...
            0 0 1115
            分享
          •   随着将更多资源投入到生成式人工智能领域,Meta 公司已经解散了其负责任人工智能(RAI)团队。The Information 今天援引其看到的一篇内部文本爆料了这一消息。报道称,RAI 的大部分成员将转到公司的生成式人工智能产品团队,其他人则将在 Meta 的人工智能基础设施上工作。  Meta经常对外宣称要以负责任的方式开发人工智能,甚至还专门为此设立了一个页面,列出了其"负责任的人工智能支柱",包括问责制、透明度、安全性、隐私等。  The Information 的报道援引 Meta 公司代表乔恩-卡维尔(Jon Carvill)的话说,公司将"继续优...
            0 0 885
            分享
          • 今天,我们开始学习使用 Python 来操作 Excel 表格,所以我们选择OpenpyXl作为起步之路。openpyxl 是用于读取和写入 Excel 2010 xlsx / xlsm / xltx / xltm 文件的 Python 库。openpyxl三步走获取work book获取 work sheet再然后 获取单元格 进行操作保存文件安装OpenpyXlpip install OpenpyXl使用时在代码内 from openpyxl import Workbook或者from openpyxl imp...
            1 1 11418
            分享
          • 在当今软件开发领域中,泛型是一种强大的编程特性,它能够在不牺牲类型安全的前提下,实现代码的复用和灵活性。Java作为一种老牌的面向对象编程语言,在其长期的发展过程中,已经积累了丰富的泛型经验和应用场景。而Go语言作为一种相对较新的编程语言,也在不断探索和发展其泛型特性,以满足现代软件开发的需求。本文将对Java和Go语言的泛型进行比较和介绍,探讨它们的实现方式、语法特点以及适用场景,帮助读者更好地理解和应用泛型编程。随着Go语言1.18版本的发布,泛型正式成为了Go语言的一部分,填补了原本的短板。通过引入类型参数,使得函数和数据结构可以接受任意类型的参数,从而提升了代码的可复用性和灵活性。这项...
            0 0 1047
            分享
      • 51testing软件测试圈微信