静态代码扫描为整个发展组织增加价值. 无论您在开发组织中发挥的作用如何, 静态代码扫描解决方案都具有附加价值, 拥有软件开发中所需要的尖端功能, 最大限度地提高质量并管理软件产品中的风险.
背景
微服务架构模式具有服务间独立, 可独立开发部署等特点, 独立开发诱发了技术上的分离, HTTP 通信增加了问题诊断的复杂度, 对系统的功能, 性能和安全方面的质量保障带来了很大的挑战.
"
微服务架构对测试的挑战
微服务架构模式下多个独立业务服务同时开展开发工作, 每个系统都有各自的业务范围和开发周期要求, 这样一来, 下图所示的传统流程中产品经理提供需求, 需求人员进行需求分析, 开发人员进行开发, 最后交给测试人员进行测试的方法, 就无法满足测试覆盖和测试效率的要求.
相对于传统的单体模式而言, 微服务模式下对测试带来的挑战总结起来包括以下内容:
1. 微服务系统模块层次化, 需要保证模块内部代码的质量. 这种场景下传统的端到端的测试无法满足测试要求;
2. 需要保证各个微服务系统内部模块间的正确性. 系统模块间以及前端和后端通常会同时开展开发工作, 模块间或者前后端通过接口 (通常是 Restful http 接口) 进行连接, 而模块和后端往往没有界面, 为了保证各个系统单个依赖系统的正确性, 因此需要借助 Mock 技术隔离依赖的前提下进行接口级的测试;
3. 需要保证微服务系统中的接口一致性, 即契约的一致性. 需要通过契约测试手段保证契约的正确性, 进而保证同步开发过程中的前后开发的正确性和一致性;
4. 需要保障单个微服务系统的正确性. 需要进行组件级的测试进行微服务系统的正确性;
5. 需要保障整个系统的正确性. 各个微服务系统串接之后通过端到端的测试保证整体系统的正确性;
"
微服务架构下如何开展测试
针对上面提到的微服务对测试的挑战, 一方面为了保证在服务各个层级上对微服务进行全面的测试, 特别是对于分布式系统; 另一方面又要确保测试执行的效率, 这样才能保证持续集成 / 持续交付(CI/CD). 因此, 总体的测试策略采用如下解决方法:
1. 开展「质量」文化. 让开发人员建立起代码「质量」意识, 用于保障模块内部的质量;
2. 采用自动化测试手段. 在微服务架构中, 开发分解为负责不同服务的多个小组, 测试人员往往每天要花费大量的时间, 了解不同团队的开发进度. 如果还需要手动进行回归测试(Regression Test), 最终将会不堪重负. 所以自动化测试在微服务模式下是必须采取的手段.
3. 分层的自动化测试策略. 自动化测试分层在 Mike Cohn 提出的测试金字塔 (Test Pyramid) 原理中进行了详细的阐述. 它提倡在代码级, 接口级, 应用级进行不同粒度的测试来保证系统的质量. 从自动化测试投入比例来看, 单元测试和静态代码扫描的投入比例最大, 其次是接口自动化测试, 最后是 UI 自动化测试. 同时为了提高测试效率和测试覆盖率, 功能测试需要借助探索式测试手段开展测试.
4. 采用流水线技术进行可视化快速反馈. 由于微服务系统非常多, 这样往往会增加了运维和沟通成本, 为了提高沟通效率, 需要借助流水线的技术, 可视化查看每一个构建 (Build), 测试(Test), 部署(Deploy) 过程, 快速做出质量反馈和处理决策. 通过可视化流水线最终可以实现各个环节的监控, 采用 DevOps 手段打通业务, 开发, 测试和运维的部门墙.
下面结合分层自动化测试的思想, 首先对静态代码扫描进行介绍.
静态代码扫描
"
静态代码扫描背景
静态代码分析是指在不运行代码的方式下, 通过词法分析, 语法分析, 控制流, 数据流分析等技术对程序代码进行扫描的技术. 它的目的是验证代码是否满足规范性, 安全性, 可靠性, 可维护性的要求. 静态代码扫描处于分层自动化测试的最底层, 它和单元测试同级别. 为了保证公司代码的规范性, 安全性, 可靠性的要求, 通过定制公司级的静态代码扫描规范, 扫描规则和扫描实施流程保证实施高效落地.
"
静态代码扫描意义
为开发者
软件开发人员最终负责代码质量. 代码质量是非功能性需求的一部分, 因此是开发人员的直接责任. 代码质量不应该存在技术债务, 在开发的过程中每一步都提供反馈, 从 IDE 到发布. 这使得开发人员能够尽早做出有关代码质量的决策, 使他们能够做得更好, 并提供质量更好的软件产品.
为 DevOps
DevOps 需要确保软件的构建方式正确. DevOps 中涉及的责任很多, 其中包括支持开发流程, 自动化测试, 确保质量, 提高生产力..... 并最终实现持续部署. 良好的代码质量是实现所有这些目标的必要条件, 尽管不是充分条件. 静态代码扫描可在任何构建 / 测试 / 部署步骤中添加的代码质量检验门槛, 能够自动执行一组统一的质量标准, 从而确保组织交付更好的软件.
为管理者
代码静态扫描可降低风险并提高团队生产力. 管理人员需要能够安全地运行软件, 并且需要花费合理的投资回报. 我们的解决方案一目了然地显示了他们面临的技术债务以及他们缓解的成本. 它还具有开箱即用的功能, 可以系统地提高开发团队的可维护性和长期生产力. 这使管理人员能够以最佳成本使用风险控制方法确保其组织能够交付更好的软件.
"
静态代码扫描介绍
静态代码扫描处在特性分支开发完成之后, 具体的描述如下:
1. 开发人员从 Master 分支拉取特性分支作为开发分支;
2. 开发完特性分支后, 代码构建, 单元测试, 静态代码扫描;
3. 通过后合并到 Master 分支, 用于投产;
"
静态代码扫描流程
随行付静态代码扫描平台的具体实现是通过集成 SonarQube 平台工具, Jenkins 集成工具, IDE SonarLint 插件和 CheckStyle 本地化规则模板等开源工具, 插件集而成. 实现本地化代码的实施检测, 版本构建后的二次检测, 以及邮件反馈等功能的流程闭环, 保证投产前代码符合随行付代码规范的要求. 具体的流程如下图所示:
1. 本地化 IDE 中通过 SonarLint 插件实现和 SonarQube 平台规则, 规范的同步, 实现本地代码检查; 随行付定制化了 java 规则, xml 规则 257 条, JavaScript 规则 86 条, 用于检测代码的规范性, 代码缺陷, 漏洞, 坏味道, 重复率等信息. 并将定制化的规则放到了 SonarQube 平台上, SonrLint 插件的规则比较全面, 包括所有的 sonajava 规则和 JavaScript 规则, 为了保证本地使用定制的规则, 且同 sonarqube 中的规则一致, 需要远程连接 SonarQube 服务器, 并绑定项目. 以 Eclipse 为例, 展示 SonarQube 连接和项目绑定过程:
2. 代码提交到代码库 GitLab 中后, 在 Jenkins 中测试环境构建时, 自动触发 Sonar 扫描, 并将扫描结果发布到 SonarQube 平台. 下图为 SonarQube 展示的一个项目的结果:
3.SonarQube 平台根据质量阀的要求, 不满足质量阀要求则邮件通知开发人员.
质量阀要求:
1. 新覆盖率大于等于 80%;
2. 新增 Bugs 为 0;
3. 新增漏洞为 0;
4. 新增坏味道为 0;
4. 开发人员收到邮件后, 进行代码处理, 直到满足规范要求为止.
5. 以周为单位统计 SonarQube 平台中静态代码扫描出来的 Bugs \ 漏洞 \ 坏味道的数量, 定时自动发送周报给相关干系人, 报告中会包含问题处理情况的趋势图.
SonarQube 与规则
SonarQube 是一个用于代码质量管理的开源平台, 支持 25 + 种编程语言的质量扫描. SonqrQube 由远程机, Server 端和数据库构成. 远程客户机可以通过各种不同的分析机制, 从而将被分析的项目代码上传到 SonarQube server 并进行代码质量的管理和分析, SonarQube 还会通过 web API 将分析的结果以可视化, 可度量的方式展示给出来. 逻辑结构如下图所示:
"
SonarQube 的整合能力
SonarQube 平台中支持整合各种静态代码扫描检测工具. SonarQube 中各种代码检测工具分析对象及应用技术对比:
Java 静态分析工具 | 分析对象 | 应用技术 |
---|---|---|
CheckStyle | Java 源文件 | 缺陷模式匹配 |
FindBugs | 字节码 | 缺陷模式匹配;数据流分析 |
PMD | Java 源代码 | 缺陷模式匹配 |
CheckStyle
可以很方便的帮我们检查 Java 代码中的格式错误, 它能够自动化代码规范检查过程, 从而使得开发人员从这项重要, 但是枯燥的任务中解脱出来. 基本上都是根据开发规则定制规则. 主要涵盖以下内容:
Javadoc 注释: 检查类及方法的 Javadoc 注释
命名约定: 检查命名是否符合命名规范
标题: 检查文件是否以某些行开头
Import 语句: 检查 Import 语句是否符合定义规范
代码块大小, 即检查类, 方法等代码块的行数
空白: 检查空白符, 如 tab, 回车符等
修饰符: 修饰符号的检查, 如修饰符的定义顺序
块: 检查是否有空块或无效块
代码问题: 检查重复代码, 条件判断, 魔数等问题
类设计: 检查类的定义是否符合规范, 如构造函数的定义等问题
FindBugs
Findbugs 是一个静态分析工具, 它检查类或者 JAR 文件, 将字节码与一组缺陷模式进行对比以发现可能的问题. 主要涵盖以下内容:
Bad practice 坏的实践: 常见代码错误, 用于静态代码检查时进行缺陷模式匹配
Correctness 可能导致错误的代码, 如空指针引用等
国际化相关问题: 如错误的字符串转换
可能受到的恶意攻击, 如访问权限修饰符的定义等
多线程的正确性: 如多线程编程时常见的同步, 线程调度问题
运行时性能问题: 如由变量定义, 方法调用导致的代码低效问题
PMD
一种开源分析 Java 代码错误的工具, 其原理为使用 JavaCC 生成解析器来解析源代码并生成 AST(抽象语法树). 与其他分析工具不同的是, PMD 通过静态分析获知代码错误. 也就是说, 在不运行 Java 程序的情况下报告错误. PMD 附带了许多可以直接使用的规则, 利用这些规则可以找出 Java 源程序的许多问题, 例如:
潜在的 Bugs: 检查潜在代码错误, 如空的 try/catch/finally/switch 语句
未使用代码(Dead code): 检查未使用的变量, 参数, 方法等
可选的代码: String/StringBuffer 的滥用
复杂的表达式: 检查不必要的 if 语句, 可被 while 替代的 for 循环
重复的代码: 检查重复的代码
循环体创建新对象: 检查在循环体内实例化新对象
资源关闭: 检查 Connect,Result,Statement 等资源使用之后是否被关闭掉
此外, 用户还可以自己定义规则, 检查 Java 代码是否符合某些特定的编码规范. 例如, 你可以编写一个规则, 要求 PMD 找出所有创建 Thread 和 Socket 对象的操作.
三种工具对比
由表中可以看出几种工具对于代码检查各有侧重. 其中, Checkstyle 更偏重于代码编写格式, 及是否符合编码规范的检验, 对代码 bug 的发现功能较弱; 而 FindBugs,PMD 着重于发现代码缺陷. 在对代码缺陷检查中, 这三种工具在针对的代码缺陷类别也各有不同, 且类别之间有重叠.
"
规则定制
考虑到 Sonar Java 规则已经包含了 PMD 和 CheckStyle 规则, 因此我们选择了 Sonar 的默认规则, 并对其进行了定制化. 下图中 SonarQube 中展示了部分定制化规则内容.
定制化后的规则覆盖的代码缺陷类型如下表所示(部分规则):
代码缺陷分类 | 示例 |
---|---|
引用操作 | 空指针引用 |
对象操作 | 对象比较(使用 == 而不是 equals) |
表达式复杂化 | 对于的 if 语句 |
数组使用 | 数组下标越界 |
未使用变量或代码段 | 未使用变量 |
资源回收 | I/O 未关闭 |
方法调用 | 未使用方法返回值 |
代码设计 | 空的 try/catch/finally 块 |
来源: https://juejin.im/post/5c6f9259f265da2deb6aa8d1