五、调试 Cypress 测试

调试是识别和消除软件应用中的错误的能力。了解 Cypress 的调试知识并学习如何解释 Cypress 的调试输出对于使用 Cypress 框架至关重要。Cypress 为自己能够立即反馈测试是通过还是失败而感到自豪。对于 Cypress 来说,要实现即时反馈机制,它必须在调试消息的构造方式上有效,以便为用户提供易于理解的解释。

为了能够在本章中表现出色,您需要阅读前面的章节,因为它们将帮助您获得测试如何运行、Cypress 如何工作以及我们可以运行 Cypress 测试的不同方式的知识。在这一章中,我们将着重于调试 Cypress 测试,同时通过一个测试运行程序在 headed 模式下运行它们。

虽然本章将探讨如何使用测试运行器调试 Cypress,但是 Cypress 附带了其他调试工具,我们可能不一定会在本章中介绍这些工具,因为它们在前面的章节中已经介绍过,或者不在本书的范围内。在本章中,我们将学习 Cypress 调试如何在测试运行程序中工作。为此,我们将涵盖以下主题:

  • 了解页面事件
  • 理解测试运行人员的错误
  • 了解已执行测试的时间旅行
  • 理解测试快照
  • 了解控制台调试输出
  • 特殊调试命令

一旦你完成了每一个主题,你就可以开始写这本书的第二部分了,包括使用测试驱动开发 ( TDD )的方法编写 Cypress 测试。

技术要求

本章的 GitHub 资源库可在https://GitHub . com/packt publishing/端到端-Web-Testing-with-Cypress 上找到。

本章的源代码可以在chapter-05目录中找到。

了解页面事件

Cypress 记录测试运行时发生的每个主要事件。它可以检测网址何时改变,何时点击按钮,甚至何时做出断言。页面事件捕捉测试运行时 DOM 经历的重要事件。

为了演示页面事件如何工作,我们将使用我们的 Todo 应用,就像我们在上一章中所做的那样。按照我们 GitHub 存储库中的chapter-05目录,我们将在 Cypress 集成子目录中创建我们的测试文件,并将其命名为debugging.spec.js。然后,我们将在新创建的规范文件中创建我们的测试,该文件将导航到 todo 应用,添加一个 Todo 项目,并检查在我们的 Cypress 测试运行程序中弹出的页面事件。下面的代码块将处理向我们的应用添加 todo 项:

it('can add a todo', () => {
      cy.get(".new-todo").type("New Todo {Enter}");
      cy.get(".todo-list").find('li').should('have.length', 
      1)
 });

在这个测试中,我们添加了一个待办事项,并检查我们添加的项目是否可以从待办事项列表中查看。以下屏幕截图显示了 XHR 页面事件:

Figure 5.1 – XHR page event

图 5.1–XHR 页面事件

前面的截图显示了前面测试的部分命令日志。突出显示的部分名为xhr,是在 Cypress 加载新页面的页面事件。页面事件是由 Cypress 机制自动检测到的,并被自动记录下来——不是作为需要执行的命令,而是作为触发应用状态变化的事件。

Cypress 记录以下页面事件:

  • 提交表格
  • 加载新页面
  • XHR 请求网络通话
  • 测试 URL 的哈希更改

为了识别 Cypress 页面事件,我们需要在 Cypress 命令日志中寻找灰色且没有任何编号的日志,例如正在执行的 Cypress 测试中的命令。

回顾–了解页面事件

在这一节中,我们介绍了什么是页面事件,何时以及如何记录它们,以及如何在 Cypress 中识别它们。我们还了解到,页面事件对于跟踪测试执行时发生的主要事件非常有用。在下一节中,我们将研究当测试抛出错误时,如何获得进一步的调试信息。我们将通过理解可能引发的错误消息来做到这一点。

理解测试员的错误

在这一节中,我们将剖析测试运行器上的 Cypress 错误,从而解释 Cypress 抛出的错误的内容以及如何解释它们。我们将介绍 Cypress 错误中存在的不同类型的信息,包括错误名称、错误消息、代码框架文件、堆栈跟踪、打印到控制台选项,并了解更多信息。了解 Cypress 中的错误不仅有助于我们编写更好的测试,还可以在测试失败时指导我们完成调试过程。

在测试失败事件中记录异常时,Cypress 表现出色。Cypress 不仅记录了哪些测试失败的信息,而且深入到遇到的错误的具体信息中。在 Cypress 命令日志中可以看到成功执行测试等错误,并提供了可能导致遇到错误的描述性信息。有时,Cypress 甚至会在命令日志中打印出解决错误需要做什么的建议。

在这一节中,我们将向debugging.spec.js添加一个测试,当它在 Cypress 中运行时会抛出一个错误。在接下来的测试中,我们将探究 Cypress 在遇到错误时提供的信息,并尝试理解为什么该信息与调试过程相关:

it('Error Test: can add a todo', () => {
      cy.get(".new-todo").type("New Todo {Enter}");
      cy.get(".todo-list").find('li').should('have.length', 
      2)
 });

这个测试应该故意抛出一个错误,因为我们期望待办事项的数量等于2,尽管我们只添加了一个名为New Todo的待办事项。

Cypress 抛出的每个错误都包含以下信息。这些将帮助您确定问题来自哪里,以及导致 Cypress 抛出错误的原因:

  • 错误名称
  • 错误信息
  • 码框文件
  • 码框
  • 堆栈跟踪
  • 打印到控制台选项
  • 了解更多(可选)

让我们详细看看这些。

错误名称

Cypress 抛出不同种类的错误,这取决于它遇到的错误。 Cypress 中的错误通过它们的类型来识别,并且它们可以通过类型来分类,例如 Cypress 错误和断言错误等等。Cypress 抛出的错误类型有助于调试。这是因为我们可以从正在运行的测试或 Cypress 内部遇到的错误中完全理解测试是否失败。该错误显示在图 5.2 中,参考为 1 ,错误名称为。

错误信息

每一个错误都会带来一个信息。该消息详细解释了测试运行时出现的错误。错误消息因测试而异。虽然有些消息可能会直接告诉你哪里出了问题,但其他消息会更进一步,甚至详细说明你可以采取哪些步骤来修复错误。一些错误信息包含一个了解更多信息部分,该部分将引导您找到与遇到的错误相关的 Cypress 文档。该错误信息显示在 2 引用的图 5.2 中。

代码框架文件

这是包含 Cypress 遇到的错误的文件。该文件显示为堆栈跟踪的最上面的项。代码框架文件显示有在 Cypress 错误框架中高亮显示的行号和列号。单击堆栈跟踪上的代码框架文件时,如果用于打开文件的编辑器支持代码高亮显示,它将在首选编辑器中打开,并高亮显示出现错误的行和列。我们可以在图 5.2 中看到代码框架文件,编号为 3

码框

这是 Cypress 标记为发生错误的原因的代码片段。可以在前面提到的代码框架文件中找到。Cypress 突出显示了代码框架片段中执行测试有问题的特定行,以及列。我们可以通过检查图 5.2 中引用为 4 的代码片段来识别导致失败的代码框架。

堆栈跟踪

堆栈跟踪显示错误发生时正在执行的不同方法,导致异常。在 Cypress 错误中,您可以切换堆栈跟踪,它可以在错误的代码框架下面找到。这将向您显示测试遇到错误并失败时正在执行的功能。图 5.2 中的 5 号显示堆栈跟踪区域。

打印到控制台

Cypress 错误还为您提供了打印开发工具控制台遇到的错误的选项。将遇到的错误打印到命令提示符的选项允许我们在堆栈跟踪中选择一行,并将其打印到控制台。我们可以在图 5.2 中看到 6

了解更多信息

正如我们前面提到的,一些测试失败会打印出一个了解更多信息链接,当点击该链接时,我们会得到关于所发生错误的相关 Cypress 文档的指示。当错误可能不仅仅需要正在调整的断言或正在测试的预期时,Cypress 故障提供了“了解更多”链接:

Figure 5.2 – Information that's present for test errors

图 5.2–测试错误信息

前面的截图显示了当测试抛出异常时显示的错误信息的时间结构。如我们所见,测试只向待办事项列表中添加了一个待办事项,但期望找到两个。错误发生在测试断言上,因为 Cypress 需要两个项目,但是只找到了一个,导致了错误。

失败测试提供的信息对调试过程至关重要。这不仅是因为识别测试失败的原因变得很容易,而且它还帮助我们理解需要在哪里进行更改,以便我们将测试从失败状态恢复到通过状态。

重述–理解测试员的错误

在本节中,我们了解了 Cypress 错误的信息量有多大。我们必须调查嵌在 Cypress 错误消息中的不同信息片段及其在调试过程中的用途。了解 Cypress 在错误发生时是如何呈现的,可以让我们知道如何处理 Cypress 的错误,了解这些错误来自哪里。在下一节中,我们将看看 Cypress 的时间旅行特征。

了解已执行测试的时间旅行

时间旅行,就像科幻电影中,但现在在测试的背景下,是一种能力,移动回一个测试在执行时的状态。随着 Cypress 测试的执行,他们创建了 DOM 快照,我们可以使用这些快照来回溯时间,并在不同的时间和不同的动作发生时检查我们测试的状态。随着时间的推移,有可能检查预期的行动是否发生以及它是如何发生的。时间旅行还允许我们调查和审核测试运行时采取了什么措施以及为什么会出现错误。

为了研究 Cypress 测试中的时间旅行,我们将导航到本书 GitHub 存储库中的chapter-05文件夹,并在我们之前创建的debugging.spec.js文件中创建一个新的测试。下面的代码块是将添加的待办事项标记为已完成的测试。通过时间旅行,我们可以在添加待办事项时识别应用的不同状态,然后将它们标记为已完成:

it('can mark a todo as completed', () => {
      cy.get(".new-todo").type("New Todo {Enter}");
      cy.get(".new-todo").type("Another New Todo {Enter}");
      cy.get('.todo-list>li:nth-
      child(1)').find('.toggle').click();
      cy.get('.todo-list>li:nth-
      child(2)').find('.toggle').click();
    });

前面的代码块将两个待办事项添加到待办事项列表中,然后将待办事项标记为已完成。使用 Cypress 时间旅行功能,我们可以参考 Cypress 来检查当我们添加第一个待办事项甚至当我们添加第二个待办事项时的状态。通过使用时间旅行功能,如下面的截图所示,我们可以进一步验证这两个项目在被标记为完成之前都处于正确的状态,并且在这样做的过程中进行了正确的导航:

Figure 5.3 – Time travel and DOM snapshotting in a test

图 5.3–测试中的时间旅行和 DOM 快照

在前面的截图中,我们可以看到测试已经完成运行,并且已经通过。我们还可以看到,我们可以回到过去,调查在待办事项列表中点击第一个待办事项时发生了什么。由于 Cypress 可以及时返回并向我们显示特定时间点的 DOM,我们实际上可以验证为达到测试的最终结果而采取的步骤——无论是测试通过还是测试失败。显示的数字显示了 Cypress 时间旅行机制的主要部分和事件发生的顺序。

时间旅行的第一步是等待测试运行完成,然后选择您希望时间旅行回到的步骤。Cypress 不仅显示了测试步骤,还允许您将步骤的 DOM 快照固定到 Cypress 预览窗口。

选择时间旅行步骤后,我们选择的感兴趣的步骤被固定为 DOM 快照。我们可以在步骤所处的状态和动作发生后转换到的新状态中查看该步骤。这可以在前面截图的预览窗口中看到。

时间行程检查流程的第三步是在之后的之前的之间进行选择,获取 DOM 快照。在之后的之前的之间切换显示了 DOM 快照中的更改。这种切换有助于我们理解我们正在检查的 Cypress 步骤的动作是如何在那个特定的阶段改变 DOM 的。当我们完成我们的检查后,我们可以进入下一个执行步骤,并在那个特定的执行步骤确定测试的状态。

重要说明

当测试仍在执行且未通过或未通过时,Cypress 时间旅行不起作用。为了获得正确的结果,您必须等待执行完成,然后才能看到所有相关步骤的最终结果。

回顾–理解已执行测试的时间旅行

在本节中,我们学习了 Cypress 如何为我们提供时间旅行功能,以便我们可以回到 Cypress 执行测试所采取的不同步骤。在 Cypress 的时间旅行让我们可以检查 Cypress 宣布我们的测试失败或通过的步骤。我们也有机会看到时间旅行功能是如何与快照功能一起工作的,这将在下一节中介绍。

理解测试快照

在 Cypress 解释时间旅行过程时,我们简要介绍了快照的概念。但是,这并不意味着我们已经用尽了快照功能的优势。

快照是强大的,因为它们让我们可以窥见测试是如何执行的,以及测试采取的步骤,这些步骤要么导致测试中的失败状态,要么导致测试中的成功状态。当我们锁定 DOM 快照时,Cypress 会冻结测试并突出显示所采取的所有操作。固定快照允许我们检查 DOM 的状态,同时查看在该特定步骤中发生的所有事件。在前面的截图中,例如在步骤 2 中,有一个事件点击框显示第一个待办事项被点击。以下屏幕截图显示了 Cypress 如何解释测试运行时发生的事件:

Figure 5.4 – An event hitbox for a toggled todo item

图 5.4–切换的待办事项的事件点击框

上面的截图显示了正在运行的事件 hitbox。在这里,我们可以看到发生了影响 todo 应用的应用状态的点击事件。

重要说明

事件点击框是一个突出显示,在固定的 Cypress 快照上弹出,显示测试与元素交互。事件命中框可以由.click()方法等 Cypress 事件触发。

快照菜单允许我们在快照的状态之间切换。如果发生了改变 DOM 的事件,我们可以切换到改变发生前的状态,切换到改变发生后的状态。之前的快照切换将显示所选测试步骤触发的任何事件之前的状态。另一方面,切换后的将显示从所选步骤触发事件后的应用状态。下面的屏幕截图显示了固定 DOM 快照的切换,它显示了事件发生前快照的样子和事件发生后快照的样子:

Figure 5.5 – ADOM snapshot menu

图 5.5–ADOM 快照菜单

在前面的截图中,我们可以看到快照菜单项。第一个窗口状图标将隐藏或显示固定 DOM 快照上的事件点击框,而之前的之后的菜单用于显示所选步骤的 DOM 转换。单击快照菜单中的关闭图标,当被单击时,取消 DOM 快照,并将其恢复到测试的已完成步骤,没有任何固定的 DOM 快照。

重要说明

快照菜单项的之前的之后的事件如何显示取决于发生的事件。在操作已经转换了 DOM 状态的事件中,快照之前和之后都将不同。当执行的操作没有直接改变 DOM 时:测试步骤的前状态和后状态可能有相似的快照。

回顾–理解测试快照

在本节中,我们学习了 Cypress 如何在每次测试运行后将重要的调试信息存储在 DOM 快照中。我们还学习了如何利用 Cypress 快照来检查测试步骤的前后状态,然后在调试的调查过程中使用它。在下一节中,我们将学习如何利用控制台的调试输出来获取信息。

了解控制台调试输出

在本节中,我们将了解如何利用 Cypress 的控制台调试输出来了解应用状态的变化。我们将在浏览器的控制台中打开控制台输出并与之交互。理解浏览器控制台中的输出将允许我们更好地调试测试,因为我们可以调查被 Cypress 作为错误抛出的问题并快速解决它们。

Cypress 擅长提供调试信息。因为快照提供的所有信息可能都不够,所以 Cypress 提供了一个额外的步骤,以便您可以查看特定步骤的信息及其对元素的影响。要查看控制台调试输出,我们需要打开我们的开发工具。要打开我们的 Cypress 测试浏览器的开发工具控制台,我们需要遵循某些步骤,所有这些都将在下面的章节中讨论。

柔软

要在 macOS 上打开 Cypress 测试浏览器的开发工具控制台,请执行以下步骤:

  1. 在 Cypress 测试浏览器预览时,用两个手指按住触控板。
  2. 从弹出菜单中选择检查选项。
  3. 开发工具控制台选择控制台选项卡。

也可以使用选项 + J 快捷方式打开 Mac 上的开发工具菜单。

Windows/Linux OS

要在 Windows 和 Linux 操作系统上打开 Cypress 测试浏览器的开发工具控制台,请执行以下步骤:

  1. 在 Cypress 测试预览中右键单击 Cypress 测试浏览器。
  2. 从浏览器弹出菜单中选择检查选项。
  3. 开发工具控制台选择控制台选项卡。

也可以使用 Windows 操作系统或 Linux 上的Shift+Ctrl+J快捷键打开 DevTools 控制台。

看到控制台输出后,选择一个测试步骤,如下图所示:

Figure 5.6 – The debug output on a browser console

图 5.6–浏览器控制台上的调试输出

前面的屏幕截图显示了命令提示符下所选 Cypress 命令的输出。正如我们所看到的,当点击一个特定的命令步骤时,DOM 快照被固定在 Cypress 浏览器的预览屏幕上。锁定 DOM 快照允许我们不间断地与锁定快照上的元素进行交互。

在前面的截图中,我们选择了get方法和第一个待办事项,可以通过.todo-list>li:nth-child(1) CSS 选择器来识别。我们还可以看到,Cypressget方法找到第一个待办事项的 CSS 选择器,并将其切换为完成。通过控制台调试信息,我们可以看到 Cypress 在控制台上打印的附加信息,该信息与操作步骤相关,现在被固定到 DOM 上。

控制台区域,我们可以看到以下内容:

  • 命令:这是我们发布的命令。在我们的例子中,这是一个cy.get()命令。
  • 产生:这会打印被调用的命令返回的语句。在我们的例子中,它将打印出与输入相同的内容。这是因为我们没有用命令改变元素的状态。
  • 元素:打印从我们的get命令返回的元素。在我们的案例中,我们只有一个元素是通过使用我们的 CSS 选择器找到的。然而,如果我们有一个以上的元素,我们将能够看到找到的元素。
  • Selector: This refers to the CSS selector that we used to identify our todo item in the DOM.

    重要说明

    控制台上显示的信息会因发出和检查的命令不同而发生变化。这不是控制台日志中检查的所有 Cypress 命令的标准配置。

使用这个调试信息,并将其与我们前面介绍的方法中的调试信息相结合,将让您了解哪些 Cypress 测试失败以及原因。在大多数情况下,您只需要学习如何读取常见的 Cypress 错误,就可以了解错误是如何抛出的,以及为什么会出现这些错误。

回顾–理解控制台调试输出

在本节中,我们学习了如何利用 Cypress 中的控制台调试输出来理解应用状态的变化。我们还学习了如何打开和访问控制台信息并与之交互。在下一节中,我们将学习如何利用 Cypress 的特殊调试命令。

特殊调试命令

如果跳过命令不是你的事情,或者你发现很难理解时光倒流如何向你展示测试中的执行顺序,Cypress 支持你。Cypress 包含有助于调试的命令,甚至为您提供了使用普通代码调试器时的选项。我们将在本节探讨的两个命令如下:

  • cy.debug()
  • cy.pause()

使用这些 Cypress 调试命令,我们可以从测试本身了解如何调试 Cypress。这两个特殊的调试命令将允许我们在执行测试时直接控制调试过程。能够在测试本身中停止执行,这给了我们一个优势,那就是只需要调试在 Cypress 中抛出错误的特定部分。

cy.debug

默认情况下,cy.debug()命令是 Cypress 提供的开箱即用的调试命令。该命令将登录到控制台,并将记录其所链接的命令的输出。要使用cy.debug()命令,您需要将其与任何cy命令链接,或者将其用作独立的 Cypress 命令。在我们的上下文中,我们将通过链接cy.get()命令来使用该命令。

该命令在被调用时暂停测试的执行,并且还显示系统地从一个命令前进和从当前执行步骤暂停调试器的选项。实际上,调试器允许我们以我们期望的速度执行测试,同时检查当执行一个步骤时会发生什么。除了调试器界面之外,这个 Cypress 命令还在控制台输出上显示详细信息,并显示命令名称、命令类型,甚至是我们链接调试器的主题等信息。

现在,我们已经添加了两个待办事项,并检查了控制台日志和 Cypress 测试运行器预览窗格,我们可以添加调试器了。下面的代码块显示了一个将待办事项标记为完成的测试。但是,我们将在添加第二个 todo 项后打开调试器,而不是执行整个测试:

it('Special commands-debug : can mark a todo as completed', () => {
      cy.get(".new-todo").type("New Todo {Enter}");
      cy.get(".new-todo").type("Another New Todo 
      {Enter}").debug();
      cy.get('.todo-list>li:nth-
      child(1)').find('.toggle').click();
      cy.get('.todo-list>li:nth-
      child(2)').find('.toggle').click();
    });

在前面的代码块中,我们想要在添加了第二个 todo 项之后检查我们的应用的状态。下面的截图显示了添加第二个待办事项后打开的调试器:

Figure 5.7 – The debugger of a running test

图 5.7–运行测试的调试器

正如我们所看到的,调试器在添加了第二个待办事项后暂停了我们正在运行的测试。在这里,我们可以观察到,一旦调试器暂停了我们的运行测试,我们就可以以自己的速度与应用交互并检查元素。调试器就位后,我们可以看到应用状态的变化,以及控制台输出中显示的其他调试信息。一旦我们完成了状态检查,我们可以移除.debug()命令或者将其放入我们想要检查的另一行。

cy .暂停

Cypresspause命令的工作原理与cy.debug()命令非常相似,但是它可以像调试器一样独立使用,而不是被链接到其他命令。当使用pause命令时,Cypress 会减慢其执行速度,并且只在单击前进按钮时执行下一步。就像调试器一样,Cypresspause命令控制执行测试的人,并允许他们调查每个测试步骤。下面的代码块显示了一个将待办事项标记为已完成的测试。但是,在执行完成之前,我们在添加第一个待办事项后暂停测试:

it('Special commands - Pause: can mark a todo as completed', () => {
      cy.get(".new-todo").type("New Todo {Enter}");
      cy.pause();
      cy.get('.todo-list>li:nth-
      child(1)').find('.toggle').click();
 });

这里,我们添加了一个待办事项,然后暂停执行,然后将其标记为完成:

Figure 5.8 – The pause menu of a running test

图 5.8–运行测试的暂停菜单

正如我们所看到的,在添加我们的待办事项后,立即停止执行,直到我们按下暂停菜单中的前进按钮。这出现在测试命令部分的顶部。执行完所有步骤后,测试将退出,通过或失败,具体取决于执行的步骤的输出。在我们的案例中,我们有一个通过的测试——万岁!

重要说明

仅当我们调查正在运行的测试的状态或为了调试目的时,才应该使用 Cypress 特殊调试命令。它们不应用于在连续集成 ( CI 中运行的测试,因为它们可能会导致超时,并随后导致测试失败。

重述–特殊调试命令

在本节中,我们了解了可用于提供附加调试信息的 Cypress 特殊命令。我们了解到,当我们想要降低测试的执行速度时,Cypressdebugpause命令都会派上用场。我们还了解到调试命令可以作为测试运行人员提供的 Cypress 工具的补充工具,例如 DOM 快照。

总结

在这一章中,我们研究了在执行测试时调试的作用。我们确定了 Cypress 框架的一些方面,这些方面使得 Cypress 的调试过程对任何编写测试和实现 Cypress 框架的人都很有用。我们还了解到,Cypress 捆绑了不同的工具,可以用来实现不同的目的,也可以用来实现相同的目的。最主要的是,不管你遇到什么 bug,丝柏都会想办法让你识别并修复。

通过完成本章,您已经了解了 Cypress 中有哪些页面事件,如何解释 Cypress 测试运行器错误,时间旅行在执行的测试中是如何工作的,以及如何解释测试快照。您还学习了如何解释来自 Cypress 的控制台输出信息,以及如何使用两个可用的特殊调试命令。

现在我们已经了解了调试及其对测试的影响,我们可以轻松地进入本书的第二部分,这将涉及使用带有测试驱动开发 ( TDD )方法的 Cypress。在下一章中,我们将使用测试优先的方法来开发应用,在开始开发应用之前,我们将编写测试。稍后我们将使用这些测试来指导我们完成应用开发的过程。