作者 | 常雨桐
在软件开发交付过程中,难免会出现Bug。针对每一个已发现问题的Bug,完成修复工作后,我们可以对其进行全面的根本原因分析。本文从测试人员的角度,尝试梳理出一些常见的Bug根本原因分析的维度,并列举每个维度中的根本原因的例子。
一、Bug分析的维度
建议尽量用便于统计和维护的方式,记录分析的结果(比如使用Jira系统提供的label功能,下文中括号内的英文是可参考的label名称),以便周期性地进行全面的Bug分析。
每个Bug常见的可用于分析的根因维度如下:
1.Bug发现的环境 (Env)
(1) 维度定义:
描述该Bug是在什么环境中被测试人员/开发团队成员/客户/用户发现的。
(2) 分析目的:
正常来讲软件开发过程中,越早发现问题,修复问题所需要的成本也就越小。为此,需要关注开发过程中的问题是否可以在早期被发现。
分析此维度可以评估现在项目的Bug发现时机。制定针对性的改进措施,以保证Bug可以被尽早发现。
(3) 维度示例:
各个项目所拥有的测试环境并不相同,请根据实际情况来进行分类。
- 开发环境/测试环境 (参考Label名:Env_QA / Env_DEV) 。项目人员最常接触的环境,也是链条最前端的环境。大部分的Bug应该在此被发现。
- 集成环境(Env_Integration) 。常用于连接到外部或其他团队系统的环境,容易发现外部集成相关的Bug。
- 用户验收环境 / 预发布环境(Env_UAT / Env_Pre-release / Env_Rehearsal) 。客户对交付的系统做验收测试或上线前演练/回归用的环境,数据和环境配置都会更贴近生产环境。
- 生产环境 (Env_Production) 。真正提供给线上用户的环境,生产环境发现的Bug拥有较高的处理优先级。
2.Bug引入时机 (Timing)
(1) 维度定义:
描述引发该Bug的代码/配置是在什么时机或者什么活动中被引入到产品中的。
(2) 分析目的:
通过分析Bug的引入时机,有机会识别出质量保证体系中薄弱的点,或团队在开发/测试流程中的问题。
(3) 维度示例:
常见的引入时机如下:
- 开发新需求时产生的新功能的Bug(Timing_Developing_New_Requirement)。在开发新功能时,产生的新功能中的行为与需求不符的问题。
- 开发新需求时破坏原有功能 (Timing_Developing_Other_Requirement) 。在开发新功能时,破坏了原有的已验收过的功能的正确行为。
- 修复Bug破坏原有功能(Timing_Fix_Bug)。在修复其他Bug时,引入了新Bug。
- 重构破坏原有功能(Timing_Refoctor)。在对代码进行重构的过程中,导致原有功能被破坏。虽然正常来讲重构活动是会在开发新卡时一起进行,理应分在开发新功能时机,但是考虑到重构行为的特殊性,以及为了后期分析重构时是否有足够的自动化测试保证原功能正常工作。可以视情况把重构单独作为一个引入时机进行分析。
- 前人代码遗留的Bug(Timing_Legacy_Issue)。中途接手的项目,在团队入场前就存在的问题。或者超出目前项目预期范围的原有问题。一般PM会更加关注此类问题,以防团队为遗留问题花费太多effort,影响正常工作的展开和新需求的交付进度。
- 部署环境或运行脚本(Timing_Env_Deployment_or_Script)。在部署/配置环境过程中引发的问题,或直接操作数据库数据引发的问题。
- 不属于Bug(Timing_Not_Bug) 。有时会发现该问题不属于Bug,本文后面会详细叙述这种情况。
3.Bug所属的前后端/微服务/功能模块
(1) 维度定义:
描述该Bug的代码问题出现在前后端/微服务/功能模块。
(2) 分析目的:
统计前后端/微服务/功能模块的Bug比例分布,后续可以有针对性地进行补充自动化测试及分配测试资源。
(3) 维度示例:
各个项目所拥有的前后端并不相同,可以根据实际情况来进行分类。
4.Bug产生的直接代码原因 (Root Cause)
(1) 维度定义:
描述引发该Bug的代码的问题,可以与开发人员合作来分析。
(2) 分析目的:
结合下一个维度,评估团队人员上下文以及技术的掌握情况。通过进行session/文档同步上下文的方式进行查漏补缺。
(3) 维度示例:
- 代码业务逻辑错误 (RC_wrong_bussiness_logic) 。代码实现时对业务的错误理解或者遗漏,导致了代码逻辑跟业务逻辑不一致。
- 代码边界条件/Edge case未覆盖 (RC_uncovered_edge_case) 。代码实现时遗漏了某些业务场景/遗漏了对某些接口或函数的特殊返回值的处理。
- 框架或依赖功能/接口的错误使用 (RC_wrongly_use_dependency)。代码实现时使用了现有的函数,或者其他微服务提供的接口。但是对其 业务含义/调用方法/返回处理 理解有误导致的问题。
- 踩了使用的框架或依赖的原有的坑/技术债 (RC_dependency_original_issue) 。受到原有的代码或系统的设计问题影响,所产生的Bug。
- 代码/脚本实现错误 (RC_wrong_coding) 。单纯的代码写错了,比如对于函数的误用,或者写代码时的手误。
- 环境,设施,数据库配置问题 (RC_misconfiguration) 。环境/基础设施/数据库 的参数配置错误引发的问题。
- 前后端接口协议不一致 (RC_FE&BE_protocol_not_match)
- 前端排版显示问题 (RC_UI_display_issue)
- 兼容性 (RC_compatibility) 。未覆盖不同操作系统/不同设备/不同客户端/不同窗体大小的差异,引发的问题。
- 非功能性需求 - 性能问题 (RC_performance)
- 非功能性需求 - 安全问题 (RC_security)
- 非功能性需求 - 健壮性问题 (RC_robust) 。连续点击,并行,弱网等情况引发的问题。
- 技术架构升级 (RC_tech_upgrade) 。依赖的包或框架升级版本引发的问题。
5.Bug产生的人员原因 (Reason)
(1) 维度定义:
描述写出Bug代码的原因。
(2) 分析目的:
结合上一个维度,评估团队人员上下文以及技术的掌握情况。通过进行session/文档同步上下文的方式进行查漏补缺。
当分析涉及具体人员的原因时,对应人员可能害怕被追责,会不自然地产生抵抗心理。所以在我们分析人维度的根因的时候,侧重点应该是团队对于上下文的掌握情况,而不是某个成员的个体原因,为团队成员建立有安全感的氛围,这样才能保证此维度的分析能持续进行下去。
(3) 维度示例:
- 需求中业务需求不够明确 (Reason_uncovered_detail_in_requirement) 。需求的某些部分可能没有清晰地表述出期望的过程和结果,在开发的流程中,开发人员对于该部分内容团队各个成员也没有识别到该问题。导致最终验收时,实现的内容与客户/业务分析人员预先期望(或者说直觉性的期望,因为可能写需求的时候就没想到这部分内容)的内容不同。
- 需求业务理解错误 (Reason_requirement_misunderstanding) 。开发人员对于需求的业务场景理解与实际业务有偏差导致的问题。
- 未考虑到边缘用例 (Reason_unconsidered_case) 。开发时未考虑到处理某些边界值或者边缘场景导致的问题。
- 业务上下文缺失 (Reason_not_familiar_with_business_context) 。团队成员对于需求相关的系统业务上下文的了解不够全面,导致的问题。比如对于接口的业务价值不了解,从而导致接口返回错误的结果。
- 代码实现上下文缺失 (Reason_not_familiar_with_code_context) 。团队成员对于需求相关的现有系统代码结构的了解不够全面,导致的问题。比如更改现有代码时,漏掉了某个不熟悉的模块中的部分相关代码。
- 对于依赖的接口/工具细节不了解 (Reason_not_familiar_with_dependency) 。对应Root cause中的“框架或依赖功能/接口的错误使用”。
- 开发过程中的疏忽 (Reason_negligence) 。单纯的开发过程中的疏忽。
未考虑到系统健壮性或其他非功能性需求 (Reason_unconsidered_non_functional_requirements)
6.自动化测试覆盖情况 (Original Automation Test)
(1) 维度定义:
描述该Bug相关的代码的自动化测试情况,自动化测试代码为何没有发现该Bug。包括单元/接口/端到端测试。
(2) 分析目的:
适当的自动化测试覆盖与适当的运行频率可以极大地提高问题代码的反馈效率,所以此维度可以用于识别系统自动化覆盖的情况。识别出自动化测试薄弱的功能/微服务后可以单抽时间对其补充必要的自动化测试。
(3) 维度示例:
- 功能没写测试(OT_none) 。因为effort或其他原因,单纯地没写测试。
- 写了测试但是没有覆盖到边界情况(OT_not_cover_edge_case) 。写了功能对应的测试,但是未覆盖到某些边界情况。
- 测试数据跟实际数据不符(OT_data_mismatch_reality) 。写了功能对应的测试,但是所构造的数据与业务的正常数据不同,导致没有发现问题。
- 重构改动过大,原有测试无法继续使用(OT_remove_by_refactor) 。重构改动过大,导致原有功能已有的测试无法继续使用。同时重构后的新的测试代码覆盖不全。
- 测试技术/框架所限无法覆盖(OT_none_tech_limited) 。因技术/框架原因,写自动化测试的effort过大,或者无法实现自动化测试。
- 测试代码错误(OT_wrong_logic) 。单纯地测试代码错误导致未识别到Bug。
7.发现的问题不属于Bug的场景(Timing_not_bug)
有时我们最终发现看到的问题不属于系统的Bug,我们可以把这种情况单独分作一类进行分析其出现的原因(Root Cause维度)。
当某种原因出现频率过高的时候,我们也需要采取对应的行动去减少此类的问题的出现,以防在大量的调查处理工作中浪费QA及团队其他成员的时间。
示例:
- 脏数据 (RC_dirty_data) 。存在于测试环境的脏数据导致的问题,常见的脏数据的来源可能是未完全开发完成的代码,团队成员对于数据库数据的手动更新或插入。一般发现是脏数据导致的问题时,需要追查脏数据的来源。如果来源是现有代码,则需要单独建Bug处理创造脏数据的代码问题。
- 新需求 (RC_new_requirement) 。Bug所期望的系统行为并不属于任何的需求中所约定的开发内容,需要新建卡来进行交付。
- 需求问题(需求错误、遗漏) (RC_requirement) 。Bug所提及的内容与需求中所约定的开发内容一致,但是与实际业务不符,需要新建卡来进行修正。
- 无法重现 (RC_cant_reproduce) 。无法重现Bug所描述的问题,可能是瞬时的环境问题,或问题已经被无意中修复。
- 基础设施问题 (RC_unstable_env) 。由于基础设施无法工作导致的问题,比如环境/数据库无法访问。
- 外部系统不稳定 (RC_unstable_external_system) 。由于外部系统停止工作或者无法连接导致的问题。
- 依赖的卡未完成开发 (RC_dependent_story_unfinished) 。Bug所描述功能的相关卡还未完全开发完成。需要开发完后再重新进行测试。
- 设计如此 (RC_by_design) 。Bug创建者理解有误或不了解上下文,其实系统的设计与现有行为一致。
最后
虽然列出了这么多维度和原因,但是毕竟每个项目各有各的情况。所以在bug分析这件事上面,并没有适用于所有项目的模板。
但是不管分析的方式及维度如何,我们做Bug分析的目标是一致的:
- 分析根因,防止未来出现类似Bug。
- 分析流程和质量保障,提前未来Bug被发现的时机,减少修复成本。
- 分析趋势,识别项目质量风险。
所以,只要满足上面的目标而且适合项目现状的分析方式就是好的方式。
以上是我对于Bug分析维度的一些思考和归纳,欢迎大家指正或提出自己的见解。