给大家30秒的时间,一起来思考这是什么?
这是某系统登陆模块功能的初始类图。
随着现代软件的不断复杂化,代码图(Code Graphs)为测试人员提供了一种直观的方法,让复杂的代码逻辑易于理解。本文将深入探讨代码图,通过挖掘到的真实场景和实际示例,展示可视化代码图如何增强软件测试人员的能力以及如何开展测试工作。
代码图指的式用于表示代码结构、类之间关系或代码元素之间交互的图形化工具,常见类型包括类图、时序图、活动图、组件图等多。
代码图由以下两个部分组成:
节点(Nodes)表示代码元素,如类、对象、活动;
边(Edges)表示节点之间的关系,如关联、继承、依赖。
以组合关系的类图为例:
代码可视化通过图形化展示代码结构和关系,团队成员在软件测试过程中使用代码图有着诸多好处,如改善彼此之间的沟通和协作、提高效率和专注度等。这有助软件测试人员更快速迪理解代码逻辑和关系,减少阅读和理解代码的时间成本。
代码图的重要作用在于有助于缩小技术和非技术人员之间的差距。测试人员可以轻松地向经理、客户或对代码不熟悉的成员解释代码流程。
曾担任西门子软件工程师的Stelios Manioudakis以自身为例,充分说明了代码图改善沟通与协作的作用。
在早期做软件测试时,他曾参与了一个开发新健身追踪器应用的项目。他们通过睡眠追踪算法分析传感器数据,来确定睡眠阶段(浅睡眠、深睡眠)并生成睡眠报告。但在测试算法过程中,他们遇到了不少的困难,因为非技术人员很难仅通过代码来理解算法逻辑。
这时候,代码图就派上用场了。他们采用可视化算法流程以及不同传感器读数和睡眠模式计算的决策点,让产品经理和UI设计通过代码图了解需要测试的潜在场景,例如不安稳的睡眠模式或缺失的传感器数据。
除此之外,代码图还促进了与开发人员的协作。在代码审查期间,测试人员和开发人员参考代码图可以更好识别预期逻辑和实际实现之间的潜在差异。最终,健身追踪应用中的睡眠追踪算法成功通过测试,代码图功不可没。
测试人员通过代码图能够更高效地规划、编写和执行测试,减少重复性工作,从而提高测试覆盖率和质量。
以新闻推送算法为例,该算法根据用户的兴趣和互动情况,对用户进行个性化推荐。最初,测试人员需要仔细逐行检查代码,这种既耗时又费力的方法很难确定所有潜在的测试场景。但代码图以一种直观的方式将各个影响因素展示出来,例如用户偏好、帖子互动和内容新鲜度。不仅如此,代码图还提供了清晰的决策点和分支路径。
热门话题:专注于测试在信息流中优先显示热门话题的场景,确保用户不会错过热门内容。
个性化推荐:优先测试具有不同兴趣和互动的不同用户资料,以验证算法是否准确推荐相关内容。
边缘情况:突出显示了潜在的边缘情况,例如不活跃的用户或互动有限的用户。测试人员可以设计特定的测试用例,以确保算法不会在这些情况下发生故障。
显而易见,使用代码图比逐行检查代码的方法快得多。这使测试人员能够优先考虑算法的关键领域,确保全面测试,并为用户提供更可靠、更个性化的新闻推送体验。
在代码重构后或团队新成员需要查看代码时,文档能够帮助团队成员更快地理解程序流程和潜在的测试要点。
举个例子:某个电子商务平台的库存管理系统是多年前建立,如今维护变得越来越复杂,新功能也越来越难实现。在系统调整过程中,面对复杂的库存管理逻辑,代码图将从产品添加、更新到订单处理、库存水平管理直观地展示出来。
由此,测试人员能够确保调整后的库存管理系统得到高效维护,代码图其中的作用不言而喻。
沟通更清晰:使用代码图向团队新成员解释系统的功能。这些图表与代码注释一起提供了清晰简洁的概述,使其更快地掌握系统的逻辑和测试注意事项。
高效的代码审查:在代码审查期间,参考代码图和代码注释有助于尽早发现潜在问题。通过可视化更改对整体流程的影响,团队可以确保修改不会对系统的其他部分产生意外的副作用。
面向未来的维护:随着系统的发展和新功能的添加,代码图可作为宝贵的参考点。即便是未参与过该项目的测试人员也可以轻松了解现有逻辑和潜在影响区域,从而实现更高效、更有针对性的测试工作。
代码图能够揭示纯文本代码审查中可能遗漏的问题,因为越混乱的代码图表示代码越复杂,这越容易出错。
举个例子,医院的预约安排系统在更新过程中引入了一项新功能:允许患者在线重新安排预约。新功能在经过代码审查和测试用例设计后,如果没有发现重大问题即可投入使用。
然而,鉴于涉及处理冲突预约特定部分的复杂性,测试人员决定采用代码图进行进一步检查和验证。代码图展示了纯文本审查中可能忽略的潜在问题:
多个决策点:基于各种因素(如现有预约、医生空闲时间以及时间限制)的众多决策点,由于测试期间难以考虑所有可能的情况,这种复杂的分支结构意味着更高的错误风险。
隐藏逻辑:图表的复杂性质使得从视觉上理解代码逻辑变得非常困难。这引发了人们对代码中可能存在的隐藏条件或意外行为的担忧。
于是,测试人员根据代码图对新功能进行了重新调整:
优先测试:优先测试涉及冲突约会的场景。专注于可能暴露复杂逻辑中潜在错误的边缘情况和组合。
与开发人员协作:借助可视化表示,测试人员与开发人员讨论已发现的复杂性。此次协作促成了代码重构工作,简化了逻辑并降低了圈复杂度。
测试人员利用代码图能主动识别潜在问题,并与开发人员合作解决这些问题。由此,这能够确保医院预约安排系统的完善性与可靠性。
代码图与结构化编程的原则(序列、选择和重复)无缝契合。这些基本结构直接映射到特定的图形模式,从而简化了对这些常见结构的测试。
(1)简化的测试设计:结构化编程强调明确定义的结构,如序列、选择(if-else)和重复(循环)。代码图将这些结构直接映射到特定模式:
序列:一条直线节点,表示一个语句接着另一个语句
选择:具有单个入口节点、条件节点和两个传出边(一个表示真,一个表示假)的分支结构,可导致单独的语句序列
重复:具有入口节点、条件节点、返回条件节点的边以及指向循环主体(语句序列)的边的循环模式
(2) 更轻松地实现测试用例可视化:通过识别代码图中这些熟悉的模式,测试人员可以快速了解程序的流程和相应的测试用例。例如,图中的循环模式表示需要测试用例覆盖循环的各种迭代,包括边界条件和预期行为。
循环数是基于代码图复杂度的度量,有助于评估程序测试的难度。复杂度越高(路径越多),测试就越彻底。但让我们深入了解更多细节。
循环数:该指标源自代码图的结构,用于估计程序中独立执行路径的数量。循环数越高,复杂度越高,这通常是由于嵌套循环、多个决策点或 GOTO 语句等因素造成的。
明智的测试规划:循环数可作为测试人员的指南,表明全面测试所需的工作量。与循环数较低的程序相比,循环数较高的程序需要更多测试用例来覆盖所有潜在执行路径。这有助于测试人员优先考虑工作,并确保全面覆盖复杂部分。
这里有一个简单的例子,考虑一个具有两个连续的 if-else 语句的简单程序:
if condition1:
# statements for if condition1 is true
else:
if condition2:
# statements for if condition2 is true
else:
# statements for both conditions false
节点:
节点1:程序的起点,代表代码执行的开始。
节点 2:决策节点condition1。此节点评估条件并根据结果(真或假)确定执行流程。
节点 3:如果为真则执行的语句块condition1。此节点表示 的“if”块内的所有代码condition1。
节点 4:与condition1相关联的 "else "节点。该节点表示在不检查condition2的情况下(即condition1为假)的替代路径。
节点 5:condition2的决策节点。此节点对条件进行评估,并根据结果(真或假)决定执行流程。
节点 6:condition2为真时执行的语句块。该节点表示condition2的 "if "代码块中的所有代码。
节点 7:与之关联的“else”节点condition2- 此节点表示程序的终点,表示如果condition1和condition2均为假时执行的代码。
边:
边 1:连接节点 1 和节点 2,表示从起点到第一个决策点的初始流
边 2(真):condition1连接节点 2 和节点 3,表示如果为真则执行的流程
边 3(假):condition1连接节点 2 和节点 4,表示如果为假则采用替代流
边 4:连接节点 4 和节点 5,表示从“else”块流向condition1第二个决策点
边 5(真):condition2连接节点 5 和节点 6,表示如果为真则执行的流程
边 6(假):condition2连接节点 5 和节点 7,表示为假时的端点
相应的代码图将具有包含三个决策点和多个执行路径的分支结构。此图的循环数为 4(节点 - 边 + 2)。与结构更简单的程序相比,这表明逻辑可能更复杂,并且需要更多测试用例。
通过了解这些好处,软件测试人员可以利用代码图来有效地导航程序逻辑,设计有效的测试用例,并有助于交付高质量的软件。
让我们详细解释一下如何得出圈数 4。
循环复杂度告诉我们程序中有多少条独立路径。路径越多,测试就越复杂。
在此代码中,我们有两个决策(检查condition1和condition2)。每个决策都会在路径中创建一个潜在的分叉(真或假)。
但是,由于else的区块condition1直接指向 的决策condition2,因此那里没有真正的分支。它就像一条通往另一个决策点的单行道。
因此,我们只计算独立的决策点:初始点、condition1(对或错)、condition2(对或错)
由于我们有 3 个决策点,但由于起点而加 1 是计算循环复杂度的常用方法,因此最终的数字是 3 + 1 = 4。
记住:
较高的圈复杂度并不一定意味着代码不好,但它表明可能需要考虑更多测试场景。
由于复杂度为 4,此代码片段并不太复杂,但随着判定数量和条件嵌套的增加,圈复杂度和测试工作量会显著增加。
虽然代码图具有改善沟通与协作、提高效率与专注度等优点,但测试人员还要需要注意以下事项:
忽略注释和声明:代码图主要关注程序内的控制流,由可执行语句表示。注释和数据声明等无法执行元素通常会被忽略,因为它们不会直接影响代码执行。
可能造成误解:虽然忽略这些元素可以简化图表,但可能会造成一些误解。测试人员需要注意这些忽略掉的元素,并确保在测试过程考虑到它们,以避免忽略与数据初始化、逻辑注释或其他不可执行代码部分相关的潜在问题。
识别有意义路径的挑战:代码图中的所有路径并非都代表有效或有意义的执行序列。根据图结构,某些路径在技术上是可行的(拓扑上可行),但在程序逻辑的背景下是不合逻辑或无意义的(语义上不可行)。
增加测试工作量:确定并优先考虑可行的测试路径可能具有挑战性,需要测试人员付出更多努力。他们需要分析程序逻辑和上下文以区分有效路径和无效路径,这可能会导致额外的测试用例设计和执行时间。
与其他测试技术相结合:代码图与其他测试技术(如代码审查或数据流分析)相结合时效果最佳。这些技术可以帮助识别不可执行元素及其潜在影响,同时还有助于理解程序的逻辑,从而更好地评估路径的可行性。
关注关键路径:测试人员可以优先测试代码图中的高风险或关键路径。这涉及考虑循环条件、预期用户输入和潜在错误场景等因素,以确定对测试影响最大的路径。
通过理解和解决这些限制,测试人员可以有效地利用代码图。在承认其固有的局限性以及将其与其他测试方法相结合的必要性的同时,可以进行全面而高效的测试。
禅道软件团队自主研发的企业IM软件中集成了开源应用draw.oi,将聊天和协同有机结合在一起,文中所有代码图均用此功能完成。通过利用代码图,软件测试人员可以更深入地了解程序逻辑。他们可以设计更有效的测试用例,并为交付高质量的软件做出贡献。
我们必须承认一点,随着软件格局的不断发展,代码图将继续成为确保应用程序的可靠性和稳健性的重要工具。
【参考资料:Stelios Maniooudakis 的 Code Graphs: A Guide for Testers】