软件测试与UML测试框架详解
1. 代码故障处理与回归测试
1.1 代码清单的局限性
代码清单虽然有用,但容易出错且非常耗时。这是因为整个过程完全依赖手动操作,并且代码的实际执行路径并未通过执行来确认。因此,只有在万不得已的情况下才应使用代码清单。
1.2 最佳故障处理方法
使用最佳方法时,需要先找出代码故障,然后绘制出修复后的代码。利用检查覆盖率所获得的信息,确保测试能够全面检测修复情况及其影响。这种额外的测试层可以确保故障得到修复。单独进行故障特征描述或代码映射都不足以完成完整的工作,但将两者结合起来则非常有效。
1.3 回归测试结果检查
回归测试中最常见的失败原因之一是对测试结果的检查不足。在实施回归测试时,应确保如果软件产生错误结果,测试能够失败。不正确的输出与数据格式错误和软件崩溃一样常见。输出往往未被检查,原因可能是难以以有意义的方式捕获,或者手动验证结果的正确性非常耗时。
对于具有GUI界面的软件,这可能是一个常见问题。一些GUI测试工具无法将输出转换为有意义的信息,仅为用户提供有限的位图比较功能。因此,仅提供位图比较的GUI测试工具在进行有意义的回归(或功能)测试时非常值得怀疑。如果有GUI,建议投资一款能够将GUI输出转换为文本和数字形式的有意义信息的GUI测试工具。
确保回归测试不仅要运行软件,还要检查结果并验证软件是否正确执行其任务。对于某些应用程序,自动检查结果可能是一个相对简单的过程。而对于其他应用程序,则可能需要测试进行替代计算,或将捕获的程序输出与已知结果进行比较。虽然将这些功能构建到测试中需要花费更多的时间和精力,但这可以避免在发布过程后期因失败而浪费时间和引发恐慌。
1.4 回归测试的价值
进行强大的回归测试可能听起来工作量很大,但请考虑不进行回归测试的成本。尽管回归测试是成本最低的软件测试形式之一,但通常执行得非常有限,仅实现了其价值的一小部分。当测试自动化并作为正常工程构建过程的一部分运行时,它们可以在早期发挥作用,此时修复失败的成本仍然较低,并且代码在设计人员的记忆中还很新鲜。如果测试自动化做得好,它们可以无人值守运行,并以快速总结通过情况和帮助诊断失败的方式报告结果。然而,要从这些测试中获得最大收益,有必要进行投资以确保它们有效且及时。
强大的回归测试需要时间来学习,并且会在修复bug的周期中增加一些时间。但是,它可以通过减少软件必须经历的bug修复周期数量来迅速收回成本。此外,在最昂贵的时候(即接近项目进度的尾声或软件已经投入使用时),它还可以节省大量的时间和麻烦。
2. UML测试框架 - JUnit
2.1 JUnit简介
JUnit是一个轻量级的Java测试框架,由知名的面向对象专家Kent Beck和Erich Gamma编写。它是一个简单且设计良好的框架,可以从 这里 下载其源代码。如果使用Java编程,JUnit可以通过编写自测试代码来提高编程效率,并且它附带了使用文档。
2.2 JUnit的包结构
JUnit由多个包组成,其包结构可以通过包图来表示。虽然UML实际上并未定义包图,但这里使用的包图严格来说是一种特殊的类图。该包图主要关注系统的包及其依赖关系。
测试包由三个子包组成:textui、ui和framework。每个包包含多个类,其中textui和ui包都有一个TestRunner类。
Java包和依赖关系可以很容易地映射到Java的package和import语句。实际上,包是模型构造的任何分组,例如可以对用例、部署节点等进行打包。其中,类的包是最有用的一种。依赖关系通常用于表示一个元素的更改可能会导致另一个元素的更改。因此,该图表明,如果更改framework包,可能需要更改textui和ui包;但更改textui包则无需担心其他包会受到影响。
在大型系统中,理解和控制这些依赖关系尤为重要。复杂的依赖结构意味着更改会产生深远的连锁反应,简化依赖结构可以减少这些影响。包图本身并不执行任何操作,但它可以帮助你可视化依赖关系,从而指导你应该采取的行动。
需要注意的是,包图中可能会省略一些内容。例如,未提及JUnit包中的一些类,显示了与java.awt的依赖关系,但省略了与java.io和java.util等的依赖关系。这并非是粗心的绘图,而是因为应该使用图表来传达最重要的信息。对于那些认为不重要的大多数Java基础库的类和依赖关系进行了省略,但对awt进行了例外处理,因为它的线条说明了使用GUI的类。选择重要的内容虽然困难,但对于任何设计师来说都是必不可少的,因为系统的所有元素并非同等重要,一个好的图表应该反映这一点。
2.3 框架包的类图
在JUnit的三个包中,最有趣的是framework包。该包的类图是从规范视角绘制的,即从类的接口而非实现角度来看待类。这是因为该图是基于javadoc文件而非底层源代码绘制的。
2.3.1 测试用例(TestCase)
测试用例代表单个测试。在类图中,没有显示测试用例操作的所有方法或完整签名,而是选择了重要的方法。run方法继承自test,它会按顺序执行setUp、runTest和tearDown来运行测试用例。runTest是一个抽象方法,由子类实现实际的测试代码,这些代码会执行一些操作,然后使用assert和assertEquals来测试表达式或值。setUp和tearDown被定义为空方法,可以根据需要进行重写,以设置和拆除测试夹具。
类图虽然解释了一些内容,但并未描述setUp、tearDown和runTest的具体作用,这些将在后续进行说明。这也说明了使用UML的一个关键点:许多人认为模型最好通过图表和字典定义来完成,CASE工具也支持这种文档趋势。但实际上,仅通过字典定义来理解模型就像仅通过阅读一个领域的术语字典来学习该领域一样困难。散文(文字说明)对于解释事物的工作方式至关重要。因此,在编写文档时,应使用散文并辅以图表,很少有必要将字典作为单独的文档。当需要操作列表时,可以像javadoc那样从源代码生成。
2.3.2 测试结果(TestResult)
运行测试用例时,结果会存储在测试结果中。测试结果包含两个测试失败集合,通过与测试失败的两条关联线表示。每条线都有一个名称和一个星号,表示这是一个多值关联,即每个测试结果可能有多个失败。failures集合包含断言失败的详细信息,表明测试失败;errors集合包含代码中任何未处理的异常的详细信息,这些异常可能导致崩溃。每个测试失败记录了测试和引发它的异常,通过关联显示链接,在这个例子中,使用“1”表示每个测试失败中只有一个测试和一个可抛出对象。通过将throwable放在包符号内,表明它位于不同的包中。
2.3.3 测试套件(TestSuite)
可以将测试用例收集到测试套件中。类图显示了《设计模式:可复用面向对象软件的元素》(Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides著,Addison Wesley,1994年)中描述的组合模式的特征形状。测试套件可以包含测试,这些测试可以是测试用例或其他测试套件。当要求测试套件运行时,它会递归地运行其所有测试。
类图中使用了刻板印象«interface»明确表明test是一个Java接口。像这样用«尖括号»括起来的语句称为刻板印象,可以用来显示核心UML中未定义的特殊信息。实现关系(虚线泛化)显示了测试用例和测试套件对接口的实现。在规范模型中,可能不需要区分子类化和接口实现。从接口的角度看,泛化仅意味着子类型的接口符合超类型的接口。这样,你可以针对超类型编写代码,任何子类型都可以安全地替代它。这一特性在Java的子类化和接口实现中都适用,但大多数Java程序员喜欢了解接口和类之间的区别。
此外,还可以使用微软常用的棒棒糖符号来显示接口。如果有许多接口,这种符号更加紧凑,但对接口的描述能力有限。
2.4 使用交互图展示测试运行
2.4.1 序列图的使用
为了展示测试的运行过程,最好使用交互图,这里使用序列图。序列图显示了运行测试套件的工作方式。图顶部的每个框代表系统中的一个对象,对象的命名格式为“对象名称 : 类名称”,类名称是可选的,因此通常使用像“a test runner”这样的名称。每个对象都有一条向下的虚线生命线,当对象在调用栈上处于活动状态时,会出现一个细的激活框,激活框是可选的,但在这里很有用。
2.4.2 测试运行过程
测试从测试运行器开始运行,用一个传入消息表示,发送消息的对象并不重要。测试运行器创建一个新的测试结果,创建新对象通过消息进入对象框而非生命线来表示。然后,测试运行器告诉套件运行,并将测试结果作为参数传递。测试结果作为收集参数,用于收集套件中所有测试的信息。
套件会调用其所有测试的run方法。序列图中显示了三个测试用例,分别代表成功、错误和失败的情况。
- 成功情况 :在成功的测试运行中,没有发生异常,测试用例正常返回。
- 错误情况 :当出现错误时,某个地方会抛出异常,run方法会处理该异常。处理程序会调用测试结果中的add error方法。UML使用虚线定义结果的返回,但未定义异常的符号,这里使用标记有特殊语句«exception»的返回线表示异常。
- 失败情况 :当测试失败时,测试会调用assert方法来检查值。对同一对象的调用显示为额外的激活。如果值不正确,assert方法会抛出一个AssertionFailed异常,run方法会捕获该异常。处理程序然后会向测试结果中添加一个失败记录。
2.4.3 序列图的优缺点
序列图的优点是能够很好地说明一组类如何交互以完成某项任务,但它在定义所使用的逻辑方面表现不佳。图中没有显示条件逻辑是如何定义的,仅仅展示了一种可能的场景。虽然有表示条件逻辑的符号,但使用它们会破坏序列图的清晰性。
序列图的复杂度通常不宜过高,如果对象过多或复杂度增加,建议将交互图拆分为每个场景的单独图表。虽然可以将三种情况分别绘制为单独的图表,但这里认为一个图表效果更好。
交互图的一个难点是决定要显示哪些内容。如果代码结构良好,不可能显示每个方法调用,可以省略自调用。同样,需要选择最重要的部分进行显示,读者可以将图表作为指南,然后再查看源代码。
交互图的巨大价值在于,当许多对象协作时,它可以澄清正在发生的事情。在这种情况下,阅读源代码会迫使你不断在方法之间跳转,而交互图可以使主要路径更加清晰。
2.5 在设计中使用UML图
2.5.1 现有框架的解释
在这个例子中,主要使用图表来解释一个现有的测试框架,该框架并非使用UML设计。但实际上,UML图也可以用于帮助设计新的系统。与直接讨论代码相比,在白板上绘制图表来讨论设计通常更加容易。
2.5.2 不同图表的作用
- 包图 :用于讨论如何布局包和依赖关系,这对整个团队是一个强有力的指导。在未与架构师和其他团队成员讨论之前,任何人都不应添加新的包间依赖关系。
- 类图 :有助于设计类的关键职责。属性和关联表示了解事物的职责,操作则用于建议执行操作的职责。通常,会添加三到四句话来总结类的职责。详细的接口和数据结构由负责该类的开发人员决定,类图代表了团队对这些类所需的一般知识。
- 交互图 :不太倾向于使用交互图来探索设计。使用包图和类图时,团队可以在白板上进行设计,使用笔和橡皮擦探索各种替代方案。而交互图在这方面显得过于笨拙,更愿意使用CRC卡来探索类的交互。一旦使用CRC卡确定了良好的交互方式,可以用交互图的草图记录结果,或者直接进行编码。虽然个人更喜欢直接编码,但许多开发人员喜欢绘制草图来帮助记住讨论的结果。
无论如何使用这些图表,都不应将它们视为事情必须如何完成的规定,而应将它们视为当前认为事情应该如何完成的陈述。开发人员在处理代码时应自由地进行更改。当出现重大更改时,最好与团队其他成员进行沟通,通常CRC会议是快速完成此操作的最佳方式。
2.6 保持简单
在本文中,仅展示了UML的一小部分内容,主要集中在UML的关键部分。如需更多详细信息,可以参考 UML文档 ,但要注意阅读难度较大。也可以查看相关书籍,如Grady Booch等人所著的《The Unified Modeling Language User Guide》(Addison - Wesley Technology Series,1998年)。
无论对UML的掌握程度如何,都不要忘记它是一种沟通工具。从这里展示的最少符号开始,保持图表简单但富有表现力。记住,任何好的设计文档都应该简短到可以在一杯咖啡的时间内读完。对于系统的每个主要方面(这里对“主要方面”的定义比较宽泛),可以编写多个这样的文档。这样的图表应该是由图表支持的描述性散文,图表应该辅助解释,而不是相反。
总结
通过以上内容,我们详细了解了代码故障处理、回归测试的重要性以及UML测试框架JUnit的使用。在软件测试和设计过程中,合理运用这些方法和工具可以提高软件的质量和开发效率。以下是一些关键要点总结:
|要点|描述|
|----|----|
|代码故障处理|先找出故障,绘制修复代码,结合故障特征描述和代码映射,利用覆盖率信息确保测试全面|
|回归测试|确保测试不仅运行软件,还要检查结果,对于GUI软件选择合适的测试工具,自动化测试可提高效率和价值|
|JUnit框架|包结构清晰,类图展示类的接口和关系,交互图展示测试运行过程,不同图表在设计中有不同作用|
|UML使用|保持图表简单、富有表现力,作为沟通工具辅助设计和文档编写|
mermaid格式流程图展示回归测试流程:
graph LR
A[开始回归测试] --> B[运行软件]
B --> C{检查结果}
C -- 结果正确 --> D[测试通过]
C -- 结果错误 --> E[分析故障]
E --> F[修复代码]
F --> B
mermaid格式流程图展示测试套件运行流程:
graph LR
A[测试运行器启动] --> B[创建测试结果]
B --> C[调用测试套件运行]
C --> D{测试用例}
D -- 成功 --> E[正常返回]
D -- 错误 --> F[抛出异常,处理并记录错误]
D -- 失败 --> G[断言失败,记录失败]
E --> H[测试结果汇总]
F --> H
G --> H
希望这些内容能帮助你更好地理解软件测试和UML测试框架的相关知识。
3. 关键知识点回顾与操作建议
3.1 代码故障处理与回归测试要点
- 代码清单使用原则 :代码清单虽有一定作用,但因其手动操作且不确认实际执行路径,易出错且耗时,应作为最后手段使用。
- 故障处理流程 :
- 找出代码故障。
- 绘制修复后的代码。
- 利用覆盖率信息确保测试全面检测修复情况及其影响。
- 回归测试注意事项 :
- 回归测试要确保不仅运行软件,还要检查结果,验证软件是否正确执行任务。
- 对于有GUI界面的软件,选择能将GUI输出转换为有意义信息的测试工具。
- 自动化回归测试可在早期发现问题,降低修复成本,且能无人值守运行并快速总结结果。
3.2 JUnit框架使用要点
- JUnit包结构 :JUnit由textui、ui和framework三个子包组成,各包包含多个类,理解包之间的依赖关系有助于系统的维护和扩展。
- 类图解读 :
- 测试用例(TestCase)通过run方法按顺序执行setUp、runTest和tearDown。
- 测试结果(TestResult)收集测试失败信息,包括failures和errors集合。
- 测试套件(TestSuite)可包含测试用例或其他测试套件,递归运行所有测试。
- 交互图使用 :序列图能展示测试运行过程,但要注意选择重要部分显示,避免过于复杂。
3.3 UML图表使用建议
- 包图 :用于规划系统的包和依赖关系,团队成员在添加新的包间依赖关系时需进行讨论。
- 类图 :设计类的关键职责,通过属性、关联和操作体现,总结类的职责,详细实现由开发人员决定。
- 交互图 :可使用CRC卡探索类的交互,确定后可用交互图草图记录或直接编码。
4. 实际应用案例分析
4.1 代码故障处理案例
假设一个Java项目中,某个功能模块在更新后出现异常。按照故障处理流程:
1. 找出故障 :通过日志分析和调试,发现是某个方法中的逻辑错误导致数据处理异常。
2. 绘制修复代码 :对该方法进行修改,确保逻辑正确。
3. 利用覆盖率信息 :运行测试用例,检查代码覆盖率,确保修改的代码被充分测试。
4.2 回归测试案例
在一个具有GUI界面的软件项目中,进行回归测试时:
1. 选择合适的测试工具 :选用能将GUI输出转换为文本和数字信息的测试工具,方便检查结果。
2. 自动化测试 :将回归测试自动化,作为工程构建过程的一部分,在每次代码更新后自动运行。
3. 检查结果 :测试不仅运行软件,还对输出结果进行检查,确保软件功能正常。
4.3 JUnit框架应用案例
在一个Java项目中使用JUnit进行单元测试:
1. 创建测试用例 :针对项目中的每个类或方法,创建相应的测试用例。
2. 组织测试套件 :将相关的测试用例组织成测试套件,方便批量运行。
3. 运行测试 :使用JUnit的测试运行器运行测试套件,查看测试结果。
5. 总结与展望
5.1 总结
本文详细介绍了代码故障处理、回归测试以及UML测试框架JUnit的相关知识。在软件测试和设计过程中,合理运用这些方法和工具可以提高软件的质量和开发效率。关键要点总结如下:
|类别|要点|
|----|----|
|代码故障处理|遵循故障处理流程,结合故障特征描述和代码映射,利用覆盖率信息确保测试全面|
|回归测试|确保测试全面,选择合适的测试工具,自动化测试可提高效率和价值|
|JUnit框架|理解包结构、类图和交互图,合理组织测试用例和测试套件|
|UML图表|不同图表在设计中有不同作用,保持图表简单、富有表现力|
5.2 展望
随着软件行业的不断发展,软件测试和设计方法也在不断演进。未来,我们可以期待更智能化、自动化的测试工具和方法,进一步提高软件的质量和开发效率。同时,UML作为一种强大的沟通工具,将在软件设计和文档编写中发挥更重要的作用。
mermaid格式流程图展示软件测试整体流程:
graph LR
A[需求分析] --> B[设计测试用例]
B --> C[执行测试]
C --> D{测试结果}
D -- 通过 --> E[软件发布]
D -- 不通过 --> F[分析故障]
F --> G[修复代码]
G --> B
mermaid格式流程图展示JUnit测试流程:
graph LR
A[创建测试用例] --> B[组织测试套件]
B --> C[运行测试套件]
C --> D{测试结果}
D -- 通过 --> E[测试结束]
D -- 不通过 --> F[分析失败原因]
F --> G[修改测试用例或代码]
G --> A
希望通过本文的介绍,能帮助大家更好地理解软件测试和UML测试框架的相关知识,在实际项目中运用这些方法和工具,提高软件的质量和开发效率。
超级会员免费看
3191

被折叠的 条评论
为什么被折叠?



