在 Cypress 测试中处理电子邮件流#frontend@twiliosendgrid

已发表: 2020-11-24

Twilio SendGrid 发送大量电子邮件。 为了发送我们所有的交易电子邮件——从密码重置到帐户电子邮件验证再到导出 CSV 电子邮件——我们使用我们自己的后端服务。

我们最近通过了发送超过 3 万亿封电子邮件的巨大里程碑。

在我们的测试环境中,我们将电子邮件发送到自托管 Squirrelmail服务器中的测试电子邮件收件箱,以避免将测试电子邮件发送到 Gmail 等实际电子邮件收件箱服务提供商。 许多重要的流程要求用户检查他们的电子邮件,单击可操作的链接,重定向回 Web 应用程序,然后在某个下载或验证成功页面继续前进。

我们通过在必要的表格中输入我们的 Squirrelmail 电子邮件地址、单击一些按钮并点击电子邮件链接来手动测试这些功能,以验证事情是否按预期工作。 我们可以在每次新代码更改时都这样做,以确保我们不会在任何地方退步,但是在端到端(E2E)测试中自动化这些步骤会很好,我们可以随时再次运行。 具体来说,我们希望使用 Cypress 编写 E2E 测试,因此我们不必每次都在自己的 Web 浏览器中手动测试这些可能缓慢且令人困惑的电子邮件流。

在我们进入帖子之前,这里有几篇您可能有兴趣首先阅读的文章。

  • 如果您之前从未编写过 E2E 测试,或者想了解在编写 E2E 测试时如何思考,您可能想在我们开始之前查看这篇博文。
  • 如果您一般不熟悉使用 Cypress 编写 E2E 测试,我们强烈建议您查看我们关于为您的 Web 应用程序实施 Cypress 测试的一千英尺概述——这将使您更好地了解 Cypress API。

这篇文章假设您知道一些 Cypress 函数,例如cy.task()来运行我们在 Node 服务器中定义的任意代码,以帮助我们处理电子邮件。 此外,如果后面的带有 TypeScript 的代码片段有点混乱,请查看我们的博客文章,了解我们如何键入 Cypress 测试。 您仍然可以通过删除类型定义并坚持仅使用 JavaScript 的语法来修改您自己的赛普拉斯测试中的代码。

我们不会介绍如何设置您自己的测试电子邮件收件箱服务器(如 Squirrelmail),但我们将专注于自动化这些与搜索电子邮件、解析匹配的电子邮件内容和跟踪电子邮件链接相关的步骤。 假设您有一个测试电子邮件收件箱服务器和您自己的凭据要连接到,这应该可以让您更好地了解使用和实现哪些功能来处理这些电子邮件流。

我们如何处理赛普拉斯测试中的电子邮件流?

为了测试整个电子邮件流,我们构建了cy.task()插件来:

  • 处理具有特定主题行的电子邮件的连接和过滤电子邮件收件箱
  • 检索匹配的电子邮件正文内容
  • 无需通过 Squirrelmail UI 登录即可从用户的收件箱中删除电子邮件

我们也走这条路,因为我们不拥有或控制 Squirrelmail UI,并且在 Cypress 测试中无法访问多个超级域,因为 Squirrelmail UI 的 URL 位于与我们部署的前端应用程序不同的超级域中.

我们首先安装了一个名为“emailjs-imap-client”的库来帮助我们设置一个 IMAP 客户端,通过一些凭据和主机配置连接到我们的 Squirrelmail 收件箱。 使用这个库,我们将所有与 Squirrelmail 相关的东西封装在一个名为squirrelmail.ts的模块中,稍后我们将在我们的plugins/index.ts中导入该模块以用于我们的cy.task()函数定义。

在涉及电子邮件的测试运行之前,我们应该删除所有具有相同主题行的电子邮件,以避免误报意外引用在先前测试中触发的旧电子邮件。 为了处理这个用例,我们实现了这个任务来删除用户收件箱中所有具有匹配主题行的电子邮件,如下所示。


在我们的测试过程中,我们触发了一个操作,该操作将导致一封电子邮件被发送到用户的 Squirrelmail 电子邮件地址,并且通常需要等待具有匹配主题行的电子邮件到达用户的电子邮件收件箱。 此过程需要几秒钟到几分钟不等,具体取决于后端进程的参与程度。 我们需要确保在它到达之前进行轮询或在测试中提供超时错误,以让我们知道邮件发送部分是否有问题或延迟。 由于我们事先已经删除了具有匹配主题行的电子邮件,因此如果它确实成功返回,我们可以确定它是从我们的测试运行中触发的。

以下是我们如何开发等待具有特定主题行的电子邮件(例如“您的电子邮件活动导出”或“发件人验证”)到达用户的电子邮件收件箱的功能。

迄今为止:

  • 我们清除了用户的电子邮件收件箱
  • 测试运行并触发一封电子邮件发送到用户的电子邮件收件箱
  • 我们成功等待电子邮件到达用户的电子邮件收件箱

现在,我们需要获取该特定电子邮件的正文内容。

幸运的是,我们可以将匹配的电子邮件正文内容作为字符串返回,稍后我们必须对其进行解析,以便操作链接返回到我们控制和拥有的 Web 应用程序。 下面的任务插件在用户的收件箱中搜索具有匹配主题行的电子邮件,并返回正文内容供我们以后使用。

作为一个简短的提醒,我们不能简单地为 Squirrelmail 页面创建页面对象,通过 UI 访问 Squirrelmail,过滤匹配的主题行,打开电子邮件,直接单击可操作的链接,然后愉快地返回我们的网络应用程序——因为我们不能在同一个赛普拉斯测试中访问多个超级域。 访问您无法控制或拥有的页面和应用程序也更像是一种反模式。

在找到我们在测试中触发的匹配电子邮件正文内容后,我们必须解析 HTML 内容,找到操作链接,触发对该链接的 HTTP 请求,然后按照重定向返回我们的 Web 应用程序。

为了解析电子邮件 HTML 内容并找到操作链接部分,我们使用了另一个名为“cheerio”的库,它加载 HTML 字符串并允许我们调用类似 jQuery 的函数来提取我们需要的操作按钮或链接。 解析出链接后,我们使用cy.request()向链接发出 HTTP 请求,按照重定向链接返回我们在一个超级域上控制和拥有的 Web 应用程序,并继续验证我们页面上的成功状态重定向到。

在您的情况下,如果您的链接已经指向正确的位置,您可能不需要触发对链接的 HTTP 请求并遵循响应的重定向。 如果链接 URL 已经直接指向您的 Web 应用程序,那么没有什么可以阻止您提取链接路径并执行cy.visit(linkPath)以重定向回您的应用程序。 对于 Twilio SendGrid 链接,如果您的电子邮件启用了链接跟踪,则链接可能看起来像“...sendgrid.net?...”,如果您启用了链接品牌,则链接可能看起来像“brandlink.com”。 这就是为什么我们需要发出 HTTP 请求并提取重定向路径来执行cy.visit(redirectPath) ,因为链接的直接“href”与我们的 Web 应用程序不匹配。

下面是使用cheerio 查找链接、向链接发出HTTP 请求并遵循重定向的示例。

结论

我们向您介绍了我们实施的许多cy.task()插件功能,以通过我们收件箱中的匹配电子邮件执行更多读取和删除操作。 我们创建这些功能是为了在网页中触发这些电子邮件流之前正确重置用户的电子邮件收件箱状态,等待电子邮件到达收件箱,最后通过链接返回其成功状态。 我们在下面总结了赛普拉斯测试的关键步骤:

  • 使用cy.task(“teardownMatchingEmails”)所有带有特定主题行的电子邮件以避免误报。
  • 通过 API 登录到用户,然后通过 UI 完成一组步骤以生成要发送到用户电子邮件收件箱的电子邮件。
  • 通过cy.task(“awaitEmailInSquirrelmailInbox”)轮询用户的电子邮件收件箱以接收具有匹配主题行的电子邮件。
  • 使用cy.task(“squirrelmailSearchBySubject”)阅读具有匹配主题行的电子邮件正文内容。
  • 通过传入电子邮件正文 HTML 字符串并使用类似 jQuery 的语法搜索元素,解析出与 Cheerio 库的正确操作链接。
  • 通过cy.request(“link”)对解析出的电子邮件链接发出 HTTP 请求,然后按照重定向响应返回 Web 应用程序,或者如果链接已经与您的超级域匹配,则使用cy.visit(“emailLinkToWebApp”)访问路径cy.visit(“emailLinkToWebApp”) .
  • 验证是否出现成功状态或在您拥有的页面上执行更多 UI 步骤。

我们希望这篇博文能鼓励您从头到尾彻底进行测试。 我们过去常常避免使用电子邮件流编写 E2E 测试,但幸运的是,我们找到了一种方法来使用这些 Cypress 测试来节省我们在手动回归测试所有内容上花费的大量时间。 我们了解到自动化和测试整个快乐路径流程而不是流程的一部分更有价值 - 除非许多步骤依赖于您不拥有或控制的第三方服务,或者无法将用户重置回一定的状态可靠。

如果您对更多与我们为 Web 应用程序编写赛普拉斯测试相关的博文感兴趣,请查看以下文章:

  • 编写 E2E 测试时要考虑什么
  • 编写柏树测试的 1,000 英尺概述
  • TypeScript 包含 Cypress 测试中的所有内容
  • 配置、组织和整合赛普拉斯测试的想法
  • 将 Cypress 测试与 Docker、Buildkite 和 CICD 集成