如何创建和使用校验点
验证点是自动化测试的一个核心功能。要创建一个验证点,我们必须首先将 AUT 驱动到我们想要验证的状态,然后我们必须检查 AUT 是否如预期那样行为,并在我们达到的点上具有正确的状态。这是通过使用验证点来完成的——这些验证点确保对象属性值或截图或 AUT 的其他可测试方面是预期的那样。
验证点可以完全通过代码使用 Squish API 的 验证函数 创建,或者使用 squishide
的点选界面以及 验证点创建器视图 或两者结合使用。
如何创建和使用属性验证
最常见的验证类型是将对象属性之一的价值与一个预期值进行比较。例如,在输入一些文本后,我们可以使用 Squish 的 test
函数之一将行编辑器的文本属性值与输入的文本进行比较,如 布尔测试.compare(value1, value2) 和 布尔测试.verify(condition)。
这样的验证点可以很容易地插入到测试脚本中,使用 squishide
的点选界面。有关如何插入属性验证点的详细分步说明,请参阅 教程:开始测试 Qt 应用程序、教程:开始测试 AWT/Swing 应用程序、教程:开始测试 Web 应用程序 等,具体取决于您使用的 GUI 工具包。
一些对象是其他对象的容器。例如,树、表格和列表小部件,以及带有菜单项和子菜单的菜单。Squish 提供了对容器对象 以及 它们所包含的对象的访问。例如,请参阅 如何在视图中测试项、小部件和模型 或 如何测试 JList、JTable 和 JTree 小部件(Java—AWT/Swing) 等,具体取决于您使用的 GUI 工具包。
如何创建和使用表格验证
使用 Squish 可以通过表格验证点来验证整个表格(以及网格和类似的控件),而不是手动在代码中这样做。此功能适用于 Qt、macOS、Java、Web 和 Windows 应用程序。
如果您正在使用 Squish 4.2 之前的版本,或者没有表格验证支持的工具包,您可以使用手写的代码达到相同的效果。有关此类代码的示例,请参阅 如何测试表格小部件和使用外部数据文件、如何测试 JTable 或 如何测试表格。
创建表格验证点的步骤与创建对象属性、截图或视觉验证所需的步骤非常相似。
在录制测试时,想要设置表格验证点时,请点击 控制栏窗口 的 插入验证 下拉按钮,选择 表格。
如果您已有现有测试脚本并且想要包含一个表格验证点,请在你想要进行验证的行的之后设置一个断点。使用多个断点可以插入多个表格验证。现在重放测试:它将在第一个断点处停止。
当测试在断点处停止时,你可以在那里插入一个新的表格验证点。为此,选择表格,在应用程序对象视图中直接,或者使用对象选择器进行视觉选择,以打开验证点创建器视图。确认验证类型组合框设置为表格。确认在应用程序对象视图中表格的复选框仍然被勾选,这样表格就能在验证点创建器中可见,并且你可以点击保存并插入验证按钮。现在可以继续录音或播放。播放停止或完成后,你可以看到已插入测试用例中的验证点。
如果表格验证失败,将在测试结果中添加一个失败条目。调用此条目的上下文菜单(例如,在许多平台上右键单击)并选择查看差异菜单项以查看差异。
默认情况下,表格验证在每个单元格上都进行精确的文本匹配,但如果这失败了,Squish会尝试对单元格内容执行正则表达式匹配。这很有用,因为在某些情况下,单元格的内容可能在每次运行中都不同(例如,时间戳值的列)。为了允许这种情况,可以替换单元格的内容为正则表达式。例如,在一个单元格中使用文本.*
意味着这个单元格将匹配任何文本,包括没有任何文本。为此,请点击测试套件视图的测试用例资源列表中的VP标签来显示可编辑的表格。然后点击一个单元格并替换其文本为正则表达式,对需要这么多单元格重复此过程。也请参见正则表达式匹配。
如何进行截图验证
本节讨论了如何创建和使用截图验证点。
如何创建和使用截图验证
尽管到目前为止最常见的验证是对象属性验证,但在某些情况下,能够直观地比较小部件(或一组小部件)如何与预期的图像显示相比是有用的。为了实现这一点,我们必须向Squish提供一张(例如,截图)显示小部件(或小部件组)以它们应有的外观的图像,这样Squish就有东西可以与之比较。
尽管Squish使设置截图验证变得非常简单,但它们并不总是像我们预期的那样有用或方便,并且在大多数情况下,使用对象属性验证比较值是最佳的方法。截图验证基本上是通过比较像素来实现的,但这很容易导致不正确地失败的验证。例如,如果测试是在与原始截图不同的机器上运行的,则测试期间拍摄的截图可能不同,如果测试是在不同的操作系统上运行或在具有不同分辨率的屏幕上运行的机器上运行的,则可能不同。即使测试是在相同的机器上运行的,如果字体或主题发生变化,则截图也会有所不同。但是,如果我们对这些问题非常小心,那么在某些情况下,截图验证是有意义的。
其中一个截图验证有用的例子是当我们需要验证一个图表或图是否正确绘制的时候。
插入截图验证与插入对象属性验证一样简单——实际上,步骤几乎相同。请参阅插入额外验证点。一种方法是设置断点并运行测试,直到达到断点;另一种方法是在测试录制期间调用验证 > 截图动作。在两种情况下,都会显示应用程序对象视图。要验证特定对象(或整个窗口)的截图,首先,选中该对象的复选框。这将打开验证点创建视图。如果您感兴趣的对象不在列表中,与AUT交互,直到显示所需对象,然后刷新应用程序对象视图或使用对象选择器选择对象。
此时尚未插入验证点。在组合框中选择截图 VP,然后点击保存并插入验证按钮。如果您从一个现有的测试开始并在断点处停止,则继续测试运行或终止它;如果在录制期间插入验证,只需继续录制和插入,直到完成。对于每个插入的验证,都会有对布尔测试函数.test.vp(name)的调用——在断点处或在录制过程中插入的位置。
显示即将添加的截图验证的squishide
当我们插入验证点时,所拍摄的任何截图以及任何对象属性值将被保存,作为预期结果的一部分。稍后,当测试脚本重放时,每当Squish达到截图验证点时,它会捕获相关小部件的截图,并将其与最初捕获的对应截图进行比较,根据图像是否相同来报告验证点的成功或失败。
如果截图验证失败,除了在日志中记录失败外,Squish还会保存新(并且不同)的截图供以后检查。在测试日志视图中右键单击失败消息也会弹出上下文菜单,并选择查看差异选项。
Squish可以使用多种技术和算法来显示截图之间的差异——例如,图像相减、并列视图以及一个动画,其中预期和实际图像快速循环显示。
图像遮罩
比较截图时,预期图像可能包含我们验证所需之外的信息。我们可以通过创建正遮罩或负遮罩来屏蔽该图像的一部分,其中正遮罩忽略遮罩以外的区域,负遮罩忽略遮罩内的区域。遮罩是在图像比较之前执行的。
有关遮罩的更多信息,请参阅设置遮罩。
如何进行视觉验证
本节讨论如何创建和使用视觉验证点。
注意:在使用XML报告生成格式时,请确保您没有使用旧版,因为需要 xml3.1
或更高版本以在测试报告中看到正确的结果。通常推荐使用最新版本。
如何创建和使用可视化验证
可视化验证可以检查对话框、整个应用窗口或其子部分在屏幕上的显示。与截图验证不同,它不会将渲染的屏幕内容视为单一表面。相反,该区域被分割,并按每个UI控件分别比较。在检查渲染像素之前,将位置、大小和内容属性与预期值进行比较。
从概念上讲,可视化验证是属性验证和截图验证的混合形式。这两类检查结合成一个单一且相当复杂的算法,该算法将一组控件视为整体,并试图找到最佳匹配。
默认情况下,所有检查将以严格的方式进行,不允许状态、布局和渲染发生变化。提供了一个编辑器,允许修改单个检查以使其更加宽容。
可视化验证有用的一例是用以验证整个对话框(或网页)仍然显示预期的文本和图像(没有更多),尽管确切的布局和UI主题可能在测试系统或AUT版本之间不同。
插入可视化验证与插入对象属性验证一样容易——事实上,步骤几乎是相同的。参见 插入附加验证点。一种方法是在设置断点后运行测试,直到达到断点;另一种方法是在录制测试期间调用 验证 > 可视 操作。在两种情况下,此时会显示 应用程序对象视图。要验证特定对象(或整个窗口)的视觉布局,首先检查对象的复选框以打开 验证点创建器视图。如果您关心的对象不在列表中,与AUT进行交互,直到显示您想要的对象,然后刷新 应用程序对象视图 或使用对象选择器选择对象。
此时验证仍未 插入!请确保在组合框中选择“可视化验证”类型,然后单击保存并插入验证按钮。如果您从一个现有的测试开始并停止在断点上,请继续执行测试或终止它;如果您在录制时插入验证,只需继续录制和插入,直到完成。对于每个插入的验证,将调用布尔测试.vp(name)函数——在断点处或在插入点所在的录制位置。
显示即将添加的可视验证的squishide
当我们插入验证点时,取到的任何截图都会与对象属性值和几何形状一起保存,作为预期结果的一部分。稍后,当测试脚本回放时,每当Squish达到视觉验证点时,它会捕获相关小部件的快照,并将其与原本捕获的对应快照进行比较,并根据快照是否被认为相同来报告验证点的成功或失败。
如果视觉验证失败,除了在日志中记录失败之外,Squish还会保存新的(不同的)快照以供后续检查。SquishIDE还提供了一个功能,允许我们在测试日志视图中的失败消息上右键单击以弹出上下文菜单,然后选择查看差异操作,这将弹出查看视觉差异对话框。
SquishIDE可以显示快照之间的差异,并允许通过更改检查来调整比较。例如,通过属性值的通配符、几何形状的容许范围、图像数据的相关阈值等。如果实际的快照是正确的(可能是因为自动测试的更改使原始布局过时),我们可以将实际快照设置为新的预期结果。
函数createVisualVP(objectNameOrReference, vpFile)是SquishIDE创建视觉验证点的替代方案。
视觉验证检查阶段
视觉等效性的最终标准是对性能到屏幕的像素级外观进行比较。然而,只有在周围条件保持稳定的情况下才能满足这一严格要求。在实践中,几个因素可能导致与完美像素准确度的偏差。
- 屏幕尺寸和分辨率
- 用户设置,如字体和系统主题
- 平台(操作系统、网页浏览器)
- 动态数据的显示(如一天中的时间等)
- UI风格更改
测试工具应能够检测此类更改并向用户警告不希望的回归。同时,工具应帮助用户获得良好的概述。例如字体大小的微小变化可能具有巨大的影响。根据用例,某些视觉外观的变化可能在可接受范围内。
因此,当执行视觉验证测试时,以下检查将按以下顺序执行
层次检查
用户界面内部元素通常作为树来建模。树结构的更改不一定导致视觉外观的改变。然而,这种变化提供了一个非常强烈的提示。这就是为什么Squish将对找到的树结构与预期进行比较的原因。
识别检查
为了执行详细的按对象分析,Squish需要将存储在视觉验证点中的每个对象映射到屏幕上找到的对象。匹配算法考虑到了对象的类型、内容属性和可选的标识属性。
内容检查
许多UI控件基于内部属性来显示屏幕上的渲染。例如,在标签中显示特定文本或切换按钮的状态。
类似于元素几何形状,此类内容属性的比较有助于找出UI更改的根本原因。
几何形状检查
如果在检测中发现像素完全一致性没有达到,仍然值得调查屏幕各个组件的情况。或许所有UI控件都存在,包括它们的视觉外观,但只是在屏幕上进行了重新排列?
视觉验证点检查包括对每个UI元素相对于屏幕位置和大小的验证。预期几何形状的偏差很可能会导致屏幕渲染的偏差。
截图检查
对每个控件各自的屏幕区域进行逐像素比较。
可以将视觉验证点配置为仅对选定的GUI元素进行像素比较。这样,原本“不稳定”的UI部分就可以从验证中排除,从而得到稳定的测试结果。网页视觉测试的一个例子:显示公司股价图表的页面部分很可能处于不断变化中。
如何在测试脚本中创建和使用属性验证点
尽管可以在不编写任何代码的情况下通过squishide插入属性验证点,但也可能在代码中直接添加属性验证。这在需要一个比点击方法更灵活的验证点时非常有用——例如,如果我们想要遍历列表视图中的每一个条目并进行验证。
从squishide的点&点击界面开始创建验证点是没有问题的,特别是因为它很容易将其转换为纯脚本代码。
当使用点&点击创建验证点时,有两种可能的记录验证的方式。默认方式(也是推荐的方式)是使用验证点创建视图的组合框中的Scriptified Properties VP选项进行插入。这将使用Squish的API进行验证,例如,使用Boolean test.compare(value1, value2)函数。另一种选择是使用Properties VP选项,这将以外部文件中保存要验证的详细信息的方式调用Boolean test.vp(name)函数。使用显式验证函数(如Boolean test.compare(value1, value2))的测试更清晰、更容易维护,因此默认使用这种方法。
要将对Boolean test.vp(name)函数的调用转换为显式验证,请单击对Boolean test.vp(name)函数的调用并启用上下文菜单。选择Scriptify Verification Point菜单选项(此选项不出现在截图验证中)。这将弹出重构向导,它显示了测试代码的“之前”和“之后”视图。如果您对提议的更改感到满意,请单击完成按钮,更改将被应用。
例如,如果我们通过squishide创建了一个(非截图)验证点——例如,检查按钮文本——并且将其插入测试脚本中,就像这样
test.vp("VP1")
如果我们将验证点脚本化,它将被替换为类似以下的代码
test.compare(waitForObjectExists(":Add_QPushButton").text, "Add")
当然,实际的验证代码将与点击方法创建的验证点匹配,并且无疑结果代码将使用测试所用的任何脚本语言。
请注意,对于手写验证而非使用 Boolean waitFor(condition) 函数,我们通常使用 Object waitForObject(objectOrName) 函数。因此,如果我们完全手动进行此类验证,我们将将其写成单行代码。例如
test.compare(waitForObject(":Add_QPushButton").text, "Add")
有关验证测试语句的脚本API的更多细节,请参阅 如何使用测试语句。
以下是一个例子,它展示了如何验证所有 Qt 列表小部件中物品的动态验证点——其内容会因运行而异——并检查没有物品的文本为空。使用 squishide
无法创建此类验证点,因为物品的数量是未知的,并且可能在每次测试运行中不同。此类灵活性只能通过使用脚本语言实现
def main(): listWidget = waitForObject(":Item Views_QListWidget") numberEmpty = 0 for row in range(listWidget.count): item = listWidget.item(row) if item.text.isEmpty(): numberEmpty += 1 if numberEmpty: test.fail("Out of %d list items, %d were empty" % ( listWidget.count, numberEmpty)) else: test.passes("Verified %d non-empty list items" % ( listWidget.count))
function main() { var listWidget = waitForObject(":Item Views_QListWidget"); var numberEmpty = 0; for (var row = 0; row < listWidget.count; ++row) { var item = listWidget.item(row); if (item.text.isEmpty()) ++numberEmpty; } if (numberEmpty) test.fail("Out of " + listWidget.count + " list items, " + numberEmpty + " were empty"); else test.pass("Verified " + listWidget.count + " non-empty list items"); }
sub main { my $listWidget = waitForObject(":Item Views_QListWidget"); my $numberEmpty = 0; for (my $row = 0; $row < $listWidget->count; ++$row) { my $item = $listWidget->item($row); if ($item->text->isEmpty()) { ++$numberEmpty; } } if (numberEmpty) { test::fail("Out of " . listWidget.count . " list items, $numberEmpty were empty"); } else { test::pass("Verified " . listWidget.count . " non-empty list items"); } }
# encoding: UTF-8 require 'squish' include Squish def main listWidget = waitForObject(":Item Views_QListWidget") numberEmpty = 0 for row in 0...listWidget.count item = listWidget.item(row) if item.text.isEmpty() numberEmpty += 1 end if numberEmpty != 0 Test.fail("Out of #{listWidget.count} list items, " + "#{numberEmpty} were empty") else Test.pass("Verified #{listWidget.count} non-empty list items") end end
proc main {} { set listWidget [waitForObject ":Item Views_QListWidget"] set numberEmpty 0 set count [property get $listWidget count] for {set row 0} {$row < $count} {incr row} { set item [invoke $listWidget item $row] if {[invoke [property get $item text] isEmpty]} { incr numberEmpty } } if {numberEmpty != 0} { test fail "Out of $count list items, $numberEmpty were empty" } else { test pass "Verified $count non-empty list items" } }
测试程序获取列表小部件的引用,然后迭代它包含的每个项目,同时记录空文本的数量。如果任何项目为空,则调用 test.fail(message) 函数;否则调用 test.pass(message) 函数。
有关如何迭代 Qt 的基于项目的方便小部件中的所有项目的示例,以及 Qt 视图使用的模型,请参阅 如何在项视图中测试项、项目小部件和模型。例如,对于 Java 测试,还有类似的章节,如 如何测试小部件 JList、JTable 和 JTree (Java—AWT/Swing) 和 如何测试 List、Table 和 Tree 小部件 (/SWT),以及 Squish 支持的其他 GUI 工具包。
©2024 The Qt Company Ltd. 本文中的文档贡献属于其相应所有者的版权。
本文提供的文档是在自由软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款下授权的。
Qt 和相应标志是芬兰及/或其他国家/地区的 The Qt Company Ltd. 的商标。所有其他商标属于其相应所有者。