Precise Testing Is Wrong — It's Just a Byproduct of Incremental Builds

There’s a popular idea in the testing world called precise testing (精准测试). In short, it is the ability to run only the tests affected by a change, instead of running the entire suite every time. I think the idea is right — but the way our industry usually implements it is wrong. In this post I’ll first explain what precise testing is and why the mainstream approach is heading in the wrong direction, and then show a different path: precise testing turns out to be nothing more than a byproduct of incremental builds. ...

2026-06-29 · 9 min · 1818 words · 翟志军 Jack Zhai

Shell Bash能力对于运维很重要吗?

Bash能力对于一个运维重要吗? 这个问题本身该不该问?我觉得是有必要的思考的。这关乎团队的能力建设。 对于这个问题的答案,我的答案是看情况。 先说一个现实中真实发生的案例。 多年前,某部门所维护的系统经常出现一些“异常”的流量。部长让架构师对20多台机器上的系统日志进行分析,以确认是否真的有“异常IP”在做坏事。 这下可难倒这位架构师了,三天时间没有搞定这个需求。 最后,过了没有多久,这位架构师就被毕业了。 纵然这位架构师被毕业的原因很多,但是他是真不会做这个事情,也是事实。 在手工运维情况下,使用Bash对日志进行分析,在运维行业里一件很常见的事情,是必备技能。 所以,这位架构师在部长面前就是“没有能力”。 这时,你觉得Shell/Bash的能力重要吗? 再说我做DevOps这几年的感受。 我经历过将手工运维升级至自动化运维的过程。这整个过程,从手工,到使用Ansible自动化部署到虚拟机,再到使用Helm实现自动化部署应用到Kubernetes。 以上的那位架构师遇到问题,在手工运维阶段,我们也遇到过。 而且这个问题,是有一个专门的“熟手”负责。他可以熟练的同时登录上多台服务器,然后在多个窗口中娴熟地敲打命令。因为只有他懂grep哪些关键字能快速找到问题,所以,团队里的成员经常找他grep排查问题。 然而,在我们将日志进行结构化后,所有有权限的人(不论开发还是运维),只要简单的学习一个sql就可以轻松统计分析日志了。 结果就是不仅这位“熟手”的生产力被释放了,团队里其他经常排队等他帮忙的人的生产力也被释放了。 在这样的场景下,Shell/Bash的能力重要吗? Shell/Bash的能力,除了被应用于日志分析,还有一个很大应用:部署。 比如以下类似的Bash脚本(示例代码是从网上获取的): group1_deploy(){ # 代码解压部署函数 writelog "group1_code_deploy" for node in ${GROUP1_LIST};do # 循环生产服务器节点列表 cluster_node_remove $node echo "group1, cluster_node_remove $node" ssh ${node} "cd /opt/webroot && tar zxf ${PKG_NAME}.tar.gz" # 分别到各web服务器节点执行压缩包解压命令 ssh ${node} "rm -f /webroot/web-demo && ln -s /opt/webroot/${PKG_NAME} /webroot/web-demo" # 整个自动化的核心,创建软连接 done scp ${CONFIG_DIR}/other/192.168.3.13.server.xml 192.168.3.13:/webroot/web-demo/server.xml # 将差异项目的配置文件scp到此web服务器并以项目结尾 } 当你所在部门还在使用这样的脚本进行自动化部署时,你就必须要有Shell/Bash的能力。但是,当你使用Ansible来实现wordpress的部署,几乎不需要写任何Bash脚本。 因此,团队里,你不需要招Bash高手,你只需要招一个刚毕业没多久的运维或者开发,就可以维护此Ansible脚本。代码如下: - hosts: all become: true # 省略部分代码 tasks: - name: ==> 0 - add host info lineinfile: dest=/etc/hosts line="10.0.0.10 {{ hostname }}" state=present - name: ==> 1 - add PPA of php7 (community) apt_repository: repo="ppa:ondrej/php" - name: add Nginx stable repository (deb) apt_repository: > repo='deb http://nginx.org/packages/ubuntu/ trusty nginx' state=present # 省略部分代码 - name: ==> 4 - install nginx, php-fpm, and php-mysql apt: name={{ item }} state=present with_items: - nginx - php7.0-fpm - php7.0-mysql - name: download wordpress tarball get_url: url: "https://tw.wordpress.org/wordpress-{{ wordpress_version }}-zh_TW.tar.gz" dest: /tmp/ - name: extract wordpress tarball unarchive: src: "/tmp/wordpress-{{ wordpress_version }}-zh_TW.tar.gz" dest: "{{ wordpress_parent_path }}" owner: "{{ wordpress_owner }}" group: "{{ wordpress_group }}" copy: no # 省略部分代码 - name: copy wordpress site conf for nginx template: src: ./templates/nginx-wordpress.conf.j2 dest: /etc/nginx/conf.d/nginx-wordpress.conf - name: fix listen.owner for php-fpm lineinfile: dest: /etc/php/7.0/fpm/pool.d/www.conf regexp: '^listen.owner\s*=.*$' line: "listen.owner=nginx" state: present - name: restart php-fpm service: name=php7.0-fpm state=restarted 在这样的场景下,Shell/Bash的能力重要吗? ...

2023-10-22 · 2 min · 219 words · 翟志军 Jack Zhai

精准测试不过是增量构建的副产品

前文中,我们给了“精准测试”定义: 它是一种能力,能只针对变更进行测试,而不是每次变更都进行全量测试。 同时,介绍了当前行业里的主流实现方法。个人并不看好该实现方法。 本文介绍的另一种实现精准测试的方法。在真正介绍前,我们就必须先说增量构建和Bazel。 全量构建与增量构建 在软件构建领域,存在两种构建类型:全量构建和增量构建。 全量构建指的是针对代码仓库中所有的代码进行构建; 增量构建是指只针对有变动的代码及受变动影响的相关代码进行重新构建。 从定义看,增量构建要做的事情和精准测试要做的事情几乎是一样的。只不过,把build命令,换成test命令罢了。 这也就是为什么我觉得应该把“精准测试”叫做“增量测试”才对。 目前行业里,在增量构建领域,Bazel可谓是佼佼者。 Bazel介绍 Bazel是Google 2015年开源的一款构建工具。采用声明式的方式定义所有的构建任务。Bazel叫target。 每个target声明包含了:构建类型、输入、构建方式、输出、依赖等。以下代码展示了两个构建任务: # 声明打成jar包,作为library被其他任务使用 java_library( name = "greeter", srcs = ["src/main/java/com/example/Greeting.java"], ) # 声明打包成一个可执行Jar java_binary( name = "ProjectRunner", srcs = ["src/main/java/com/example/ProjectRunner.java"], main_class = "com.example.ProjectRunner", # 依赖之前打好的library,这是实现增量构建的关键 deps = [":greeter"], ) Bazel在运行时,就会根据target声明,在内部维护一个有向依赖图,如下: 有了这个有向依赖图,Bazel就可以实现增量构建。 当用户修改了 Greeting.java 文件时,Bazel知道 //:greeter target 依赖它,所以Bazel知道要执行 //:greeter 时。同时Bazel又知道 //:ProjectRunner 依赖 //:greeter ,所以Bazel知道还要执行 //:ProjectRunner target。 以上是在同一个语言下的增量构建的案例,让我们看下多语言场景下,Bazel是如何实现增量构建的。 多语言场景下Bazel是如何实现增量构建的 如下图,在一个软件工程下,同时使用到了:Docker、Python、YAML、C++等技术。 这个依赖关系是在开发和运维在写代码的时候就定义好了的。所以,Bazel从一开始就有了这个依赖图。 Bazel允许声明不同语言的target之间的依赖,所以,很自然的,一个软件工程的完成的依赖图就有了。你不需要花额外的精力去收集。 当执行Bazel进行构建build时,Bazel发现配置文件config.yaml是被修改了,这时它就计算出接下来要执行的构建,如下图标为橙色的路径。即所有的依赖于//:config.yaml 的直接依赖和间接依赖。 但是,因为执行的是build,所以,Bazel只会build路径上的所有源代码,并不会去执行 *_test 测试任务。 这就是精准构建,不,叫增量构建:只构建需要构建的。 也许你会好奇Bazel是怎么做到的?请关注我的公众号,将来我还会分享更多Bazel的内容。 Bazel是如何精准测试 Bazel有很多子命令,有两个常用的子命令,一个是build,一个是test。这两个子命令是用于区分构建的类型的。因为有时,你可能只是想build,不想test。 接着上面的例子,同样修改了config.yaml文件,当我们执行的是test子命令,Bazel会计算出要执行的路径——和上文一样的路径,因为//:config.yaml所影响的依赖范围是一样的。 区别是,这次它除了执行build,还会运行*_test类的任务。Bazel并不会关心它是单元测试,还是集成测试,只关心该测试的大小。如下图中的标为黑色的部分: 这就是精准测试了。同时,Bazel还会发现//:x_test、//:main_test、//:docker_image_test是完全独立的测试,那么Bazel就可以进行并行测试,进而提升测试的速度。 精准测试是增量构建的副产品 说回我们之前总结的精准测试的实现思路: 找到变更; 根据变更找到相关联的测试用例; 只执行相关联的测试用例。 以上所有步骤都可由Bazel完成,而且可以在本地完成。 ...

2023-04-22 · 1 min · 89 words · 翟志军 Jack Zhai

精准测试是个错误

如果你已经了解了精准测试在行业的主流做法,你可以跳过相关内容。 行业里对于精准测试的定义 在网上流传着一些精准测试的定义(如果你对这些定义不感冒,可直接跳到我个人的定义): 自网易陈逸青(2020)的定义: 借助一定的技术手段、通过辅助算法对传统软件测试过程进行可视化、分析及优化的过程,使得测试过程更加可视化、智能、可信和精准。 原文:https://www.infoq.cn/article/xuu91crqa4hcjz8uomjs 来自HSBC的测试咨询专家齐磊(2021年): 通俗点讲:核心基于源代码变更分析,结合分析算法,确定影响范围,提升测试效率。 原文:https://www.infoq.cn/article/2feiv8a5kogaqlbzwosh 来自星云测试(2022年): 精准测试一句话概括就是:测试用例和代码之间的追溯,这是它最本质的东西。精准测试的本质决定了它抓住了测试的一个核心要点。 原文: https://testerhome.com/topics/34557 来自得物技术(2023年): 精准测试是基于源代码变更分析,结合一些分析算法,从而确定改动代码影响的范围,设计测试用例进行针对性测试,一方面可以提升测试效率,另一方面精准测试还可以将测试用例与程序代码之间的逻辑映射关系建立起来, 而这个过程则是通过工具去采集测试过程执行的代码逻辑及测试数据。这两个点也正是精准测试的核心:正向追溯和逆向追溯。原文: https://tech.dewu.com/article?id=43 以下是来自网易严选的架构图: ![](/assets/images/网易严选的架构图1.png]] 我个人的定义 在笔者看来,精准测试的定义应该是这样的:它是一种能力,能只针对变更进行测试,而不是每次变更都进行全量测试。注意,我指的是“变更”,而不只是“代码变更”,也就是说所有类型的变更,包括手动变更。 精准测试的思路并不复杂,分成三个步骤: 找到变更; 根据变更找到相关联的测试用例; 只执行相关联的测试用例。 其实,把这种方法叫增量测试(Incremental Testing) 更准确,更合适。毕竟你是针对增量的代码变更进行测试。 如果不是针对增量变更进行测试,你也能只执行一个你想测试的测试。难道这样不算精准测试吗? 国内行业主流的实现精准测试的方法 步骤一:找到代码变更 通过commit之间进行差异对比​。​ 步骤二:根据代码变更找到相关联的测试用例 要做到“根据代码变更找到相关联的测试用例”,我们就必须知道代码与测试用例之间的关系。 获取这个关系的做法是在执行测试的同时,做以下事情: 将流量记录下来; 将因流量而执行地代码的调用链记录下来; 将测试用例的元数据与代码调用链的关系记录下来; 这个过程就完成了对被调用代码与测试用例之间的映射关系的建立。 另,现实往往存在很多未被测试用例覆盖到的代码,这时,通过静态代码分析和测试覆盖率计算技术结合,生成未被测试到的代码的报告。 可以看出,通过以上方式“找出代码与测试用例之间的关系”的成本是极高的。所以,在这个领域会有:引流平台、测试用例管理平台、精准测试平台等等平台。这也给大家一个感觉,我们要先有一个平台才能做到精准测试。 说到底就是通过插桩技术,构建代码的执行路径,并找到​对应的测试用例之间关系。 目前在网上目前看到大多还只是针对Java语言或者C++来实现精准测试,其它的语言目前没有见到。 步骤三:只执行相关联的测试用例 当有了代码与测试用例之间的关系,只执行相关联的测试用例就简单很多了。 主流方法的坑 以下是齐磊总结的精准测试存在的问题: 基于手工测试的精准测试建立映射关系繁杂,如果需求改变频繁,用例维护以及之间的关系维护需要耗费大量时间精力。 精准测试需要一定的自动化测试的覆盖,这样做起来更有意义,例如 api 自动化测试,如果本身用例过少,与代码之间关联关系不多时,变更代码后可能不会得出什么结果。 最好有对应的用例管理系统,能够方便的帮助我们建立与代码之间的关系。 需要投入开发能力强的 QA 或者测试开发建立整套系统环境,但长远考虑,将精准测试嵌入整个公司的质量平台中,不管对于新项目还说维护项目来说都是一种提升。 项目生命周期需要较长,短期项目花费巨大精力开发和维护整套精准测试系统得不偿失。短期项目可以利用精准测试以 api 测试覆盖率作为衡量标准。不去建立繁杂的关系,只监控 UI API 测试覆盖率迭代时的变更来达到目的。 但是,个人认为齐磊总结的内容没有问题,的确都是坑。但是那些不是精准测试的坑,而是国内行业主流的实现方式的坑。直白地说就是喝水时,喝水的角度错了。 为什么主流实现方法从方向上就是错的 为什么我认为以上地坑是由实现方法导致的?以下是我的论点,欢迎讨论指正: 该方法只局限于单一语言 准确来说,精准测试不应该只针对代码的变更,而是所有的变更。更不应该只针对单一语言的变更,而是可以针对所有的语言。 因为精准测试的定义本身不局限于某种语言的代码变更,而是对一个软件工程中所有的变更而言。一次SQL的变更,你是否需要精准的知道要执行哪些测试?一个前端的CSS代码的变更,你是否需要精准的知道要执行哪些测试? 目前行业里主流的方法,只是针对单一语言下的场景而设计的。按同样的思路是无法做到多语言的。我说的多语言指提同一工程下的多语言,不是指相互独立的单语言工程。 只能在平台上做精准测试 即,我们首先需要一个平台,才能做到精准测试。 但我们希望在开发者本地开发环境就可以做到精准测试。 最后 文章标题并不是说“精准测试”本身是一个错误,是想说上述的实现方法是一个错误方法。 ...

2023-04-21 · 1 min · 74 words · 翟志军 Jack Zhai