如何利用Karate简化GraphQL服务测试

2520 篇文章 2 订阅
2357 篇文章 14 订阅

在过去的一年里,我们团队重构了现有的 GraphQL 单体架构,并转向了微服务架构,这也就是所谓的联合设置。由 Apollo 倡导的联合 GraphQL 代表了公司构建和扩展其 GraphQL 生态系统方式的重大转变,特别是在从单体设计向基于微服务的架构过渡时。

在联合设置中,每个微服务都维护着自己的 GraphQL 模式,与它所服务的领域相关。然后,这些单独的模式被无缝地拼接成一个统一的网关。这个网关作为客户端的单一访问点,使他们能够跨多个服务查询数据,就好像这些数据来自一个单一的、单体的 GraphQL API。这样的变化还涉及到测试和交付的全新方法,目标是使开发人员能够快速、安全地自主部署到生产环境。

图片

理解 GraphQL 服务和我们面临的挑战

根据官方网站的说法,GraphQL 是一种用于 API 的查询语言,也是一种利用现有数据满足这些查询的运行时。GraphQL 赋予客户端只请求他们确切需要的东西的能力,这就是它的主要优势。我们不会详细讨论,假设您已经了解 GraphQL,并且考虑到网上有丰富的文档。

测试 GraphQL 服务带来了一些挑战:

  • GraphQL 允许客户端构建复杂的查询,在单个请求中获取深度嵌套的数据我们需要确保 GraphQL 模式根据类型定义和约束正确验证查询和变更。这包括测试查询语法错误、类型不匹配,并确保查询符合模式的结构。

  • GraphQL 可以根据类型之间的关系返回嵌套的数据结构。测试必须考虑到这些关系,确保服务器正确解析它们,并在嵌套查询中保持数据完整性。

  • GraphQL 处理错误的方式与 REST 不同,在大多数情况下返回状态码 200(“OK”),即使在应用程序级别存在错误。它可以在同一响应中返回部分数据和错误。测试需要确保服务器正确报告错误,并且客户端能够处理这些混合响应。

  • GraphQL 模式作为客户端和服务器之间的契约。测试必须确保对模式的更改不会破坏现有的查询和变更,因此需要一种在不造成中断的情况下对模式进行版本控制或演进的策略。

为了解决所有这些问题,可以采用单元测试、集成测试和端到端测试的组合,以及像模式验证这样的良好实践。

从单体架构迁移后,我们所有的 GraphQL 服务都存储在一个单体仓库中。我们在单体仓库中有持续部署(CD)策略,这意味着合并到主分支的任何更改都应该部署到 Edge、Stage 和 Production 环境。我们已经有了代码中的单元测试和集成测试,但它们没有覆盖实际部署的应用程序。这对我们的开发人员来说压力很大,因为我们的某个 GraphQL 服务的错误更改可能会破坏整个网站。这个缺口由 karate 填补。

现在我们将说明我们采用了什么解决方案来确保更可靠的部署策略。

Karate 简介

当我们开始引入微服务的新测试和发布流程时,一些测试层,如单元测试,以及客户端应用程序(无论是网络、原生移动应用等)中的端到端测试已经存在。之前列出的要点中缺失的部分是一个良好的集成测试层。由于 trivago 的几个团队多年来成功地使用 Karate 框架进行 API 自动化,我们的选择是将其集成到我们的 GraphQL 服务发布流程中。它通过允许测试人员和开发人员主要用简单的英语定义测试场景来简化测试编写过程,因为 Karate 的语法受到 Gherkin 的启发,从而使用户能够编写清晰简洁的测试。此外,还可以使用一整套 JSON 路径表达式、Karate 的特定快捷方式和关键字或 JS 和 Java 函数。虽然它最初专注于 REST API 测试,但其功能已经扩展到包括更多,如 GraphQL 服务。

Karate 的一些相关关键特性:

  • 可读性语法:Karate 使用一种特定领域语言(DSL),即使对于非程序员也易于理解和编写。这促进了团队之间的协作。

  • 置断言和匹配器:Karate 内置了强大的断言和匹配器,允许用最少的代码进行复杂的验证。

  • 支持多线程:Karate 中的测试可以并行运行,显著减少测试执行时间,这对于持续集成管道至关重要。

  • 对JSON和XML的原生支持:鉴于 API 响应通常是 JSON 或 XML 格式,Karate 提供了处理这些格式的一流支持,包括验证和转换。

除了上述优点外,由于以下几个原因,Karate 特别适合测试 GraphQL 服务:

  • 灵活的查询测试:Karate 处理动态 JSON 和 XML 响应的能力使其非常适合测试 GraphQL 灵活和嵌套的查询响应。

  • 可读性和可维护性:基于 Gherkin 的 DSL 允许编写清晰且可维护的测试,这对于记录和测试 GraphQL 中无数可能的查询和变更非常有益。

  • 自定义断言:GraphQL 响应可能深度嵌套和复杂。Karate 强大的断言库可以处理这些复杂性,允许测试人员验证即使是最嵌套的数据结构。

这是一个用 Karate 为目的地服务编写的测试示例,特别是提供目的地详细信息的查询。在我们的特性文件中,我们导入所需的 GraphQL 查询(单独定义,因此也可重用)并将其分配给一个变量。请求有效负载也在 JSON 文件中定义,并以相同的方式导入。

图片

用于测试目的地服务的 Karate 特性文件示例

然后我们依靠 Karate 的断言来验证响应。正如你所看到的,在某些情况下,我们想要测试特定的预期值,而在其他情况下,我们可以轻松地遍历数组并检查字段类型,指定它们是否可为空等。有关如何使用 Karate 的更多详细信息,我们建议查看 KarateLabs 官方页面、GitHub 上的 Karate 项目。(https://github.com/karatelabs/karate)

利用 Justfiles 进行测试

Justfile 与 Just 相关联,Just 是一个任务运行器,经常被用作 make 的现代替代品。它的主要目的是保存和运行特定于项目的命令。Just 旨在方便保存和执行开发过程中所需的命令,例如构建、测试或部署您的软件。Justfile 是一种以可读、直接的语法记录这些命令的简单方法,使开发人员更容易记住和使用它们。参数化和跨平台兼容性是它的一些优点。通过使用 justfile,我们可以在一些目标中抽象出一些复杂的任务。这些目标可以在本地环境和 CI/CD 工作流中使用。这种我们称为本地优先的方法让我们能够在 CI/CD 工作流中解决问题,并且通过在本地运行目标并回滚错误更改来更快地对事件做出反应。

如前所述,我们所有的 GraphQL 服务都在一个单体仓库中。我们为每个服务考虑了一个有界上下文。GraphQL 服务(API)和与该有界上下文相关的所有服务都将存储在该目录中。

    advertisers -> Bounded Context for advertisers
    ├── advertiser-i18n-transformer -> service transformer
    └── api -> GraphQL service

Justfile 提供了一个有用的功能,让我们为所有服务和 GraphQL 服务仅使用一个共享的 Justfile,所以我们在仓库的根目录中只有一个 Justfile,其中包含所有所需的目标。当我们在服务目录(例如 advertisers/api)中运行一个目标时,Just 会在仓库根目录的 Justfile 中找到该目标。

在我们的共享 Justfile 中有多个目标,可用于在特定层(Edge、Stage、Prod)上为服务构建、部署和进行集成测试。我们为集成测试提供了两个目标。第一个是 integration-tests-dev,用于针对本地环境运行集成测试,对开发人员非常有用。第二个是 integration-tests,可用于针对任何层和区域运行集成测试。这个目标可以在本地和 CI/CD 工作流中使用。它允许我们的 QAs 和开发人员在开发测试期间针对我们的 Prod 或 Stage 运行集成测试。

依靠 Just 意味着在本地也可以通过一个命令运行测试,我们只需要指定环境,可选地指定区域,就像 just integration-tests stage 或 just integration-tests prod europe-west-4 一样简单

将 Karate 与 Docker 和 Justfiles 集成

为了在需要覆盖数十个服务的情况下实现最流畅和最轻量级的测试执行设置,我们选择了以下方案:

  • 一个主 Karate Docker 镜像,仅在必要时构建和推送,例如,如果我们想要使用新版本或添加功能。

  • 每个服务的一个 Docker 镜像,基于主镜像。

  • 每个服务的特性、查询和有效负载的文件夹,将其复制到相应的服务容器中。

下面的图示将详细解释这一切。这种方法为我们的用例提供了一个高度可维护、高效的设置。

我们有两个用于 Karate 测试的 Docker 镜像。第一个是主 Karate 镜像,其中包含测试所需的 Karate 框架和特性。第二个是持续部署的 Karate 镜像,它使用主 Karate 镜像作为基础镜像,并包含该服务的所有测试。

图片

Karate 测试的 Docker 镜像

正如我们提到的,主 Karate 镜像是所有 GraphQL 服务 Karate 镜像的基础镜像,这意味着每当我们更新主 Karate 镜像时,我们需要更新 50 多个 Dockerfile。但实际上并非如此,因为多亏了 Justfile,我们在 Justfile 中以变量的形式共享主 Karate 镜像的版本,并且在构建 GraphQL 服务时,它将被注入并用于构建 GraphQL 服务的 Karate 镜像。

我们使用 Justfile 来构建主 Karate 镜像,目前这是一个手动过程,可以由质量保证工程师或站点可靠性工程师完成。构建的输出将为我们提供一个镜像标签,可用于更新 Justfile 中主 Karate 镜像的版本。

尽管 Karate 有自己相当不错的报告解决方案,但我们的测试报告是使用我们在其他项目中也使用的 Cluecumber 插件生成的,并推送到 GCS 桶,这也是之前文章中提到的解决方案。当测试在分支的预览环境中运行时,或者在部署到生产环境时作为 Slack 通知中的链接,这些报告的链接会在 GitHub 上的拉取请求的评论中提供。

我们可靠的发布策略

我们开始考虑一个发布策略,以增加保护并让我们尽快检测到错误更改。我们实施了自己的蓝绿策略,这让我们能够在将 GraphQL 服务部署到 Stage 和 Production 环境之前对其进行测试。

Stage 和 Production 环境对我们非常重要。对于 Production 环境这是显而易见的,因为它为我们的用户服务,但 Stage 环境也很重要,因为开发人员使用它来开发新功能,QAs 使用它对这些新功能进行端到端测试。为了明确起见,我们的主网站有一个 Stage 环境,该环境在 Stage 环境中使用所有的 GraphQL 服务,因此,如果我们在 Stage 环境中有一个损坏的 GraphQL 服务,可能会导致开发和测试功能出现问题,影响其他团队。因此,对我们来说,Stage 是 Production 环境的一个合适副本,也应该高度可靠和可用。

在将更改部署到 Stage 和 Production 环境之前,我们需要测试这些更改。首先,我们应该将这些更改部署到我们的 Stage 和 Production K8S 集群。为了做到这一点,我们引入了两个新环境,称为 PreStage 和 PreProd。PreStage 和 PreProd 是我们的 Stage 和 Production K8S 集群中的临时环境。

对于 PreStage,我们将 GraphQL 服务及其所有基础设施组件(如 Istio Virtual Service 等)部署到 Stage K8S 集群上的一个单独的 K8S 命名空间。对于 PreProd,我们将 GraphQL 服务及其所有基础设施组件部署到所有 Prod K8S 集群上的一个单独的 K8S 命名空间。然后,我们针对这些环境运行集成测试。这种方法让我们能够一起测试基础设施和服务,因为基础设施方面的错误更改将导致服务无法访问甚至无法运行。

当我们更改任何 GraphQL 服务时,在将该更改部署到 Production 环境之前,以下步骤应成功通过:

  1. 检测更改的 GraphQL 服务

  2. 为 GraphQL 服务及其 Karate 测试构建 Docker 镜像

  3. 将服务部署到 Edge 环境

  4. 将服务部署到 PreStage 环境

  5. 针对 PreStage 环境运行 Karate 集成测试

  6. 销毁 PreStage 环境

  7. 如果集成测试通过,将服务部署到 Stage 环境

  8. 将服务部署到 PreProd 环境(所有区域)

  9. 针对 PreProd 环境运行 Karate 集成测试

  10. 销毁 PreProd 环境

  11. 如果所有区域的所有集成测试都通过,将服务部署到 Prod 环境

  12. GraphQL 服务的发布和测试步骤

图片

有人问我们为什么需要 PreProd 环境,考虑到我们可以在 PreStage 环境中测试更改。我们的 GraphQL 服务依赖于其他服务和组件,所以我们想要确保与这些下游服务的工作连接,然后能够测试服务及其所有依赖项,为此我们在 Stage 和 Production 之间可能有不同的配置。

除了在 PreStage 和 PreProd 环境上的集成测试外,我们为每个拉取请求提供了一个预览环境。在预览环境的 CI/CD 中,在构建和单元测试之后,服务将被部署到 K8S 集群上,并对其运行集成测试。这样,我们的开发人员可以更有信心地合并他们的拉取请求。

在所有集成测试步骤中,无论是在生产发布还是预览环境中,我们都使用我们的 just integration-tests 命令,就像在本地使用的方式一样。

结论

总之,Karate 具有可读性强的 DSL、强大的验证工具以及包括并行执行在内的全面测试能力,使其成为测试 GraphQL 服务中复杂和多样交互的有力选择。通过采用 Docker 和 Just 等工具的适当组合,我们还降低了复杂性,并提高了一个解决方案的可靠性和可维护性,该方案现在为每天多次部署的数十个微服务提供了自动化测试覆盖。我们成功设置了多个关卡,能够在代码到达生产环境并对用户产生负面影响之前检测到问题。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Karate 是一个基于 Cucumber 的开源自动化测试框架,可用于测试 API、Web、UI 等各种应用程序。以下是 Karate 的搭建步骤: 1. 安装 Java 开发环境(JDK) 确保已经安装了 Java 开发环境(JDK),可以在终端输入 java -version 命令查看是否安装成功。 2. 安装 Maven 下载并安装 Maven,可以在终端输入 mvn -version 命令查看是否安装成功。 3. 创建 Maven 项目 在终端中进入任意目录,执行如下命令: ``` mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false ``` 其中,groupId 是项目组织标识符,artifactId 是项目名称。 4. 添加 Karate 依赖 在项目根目录下的 pom.xml 文件中添加 Karate 的依赖: ``` <dependency> <groupId>com.intuit.karate</groupId> <artifactId>karate-junit5</artifactId> <version>1.1.0</version> <scope>test</scope> </dependency> ``` 5. 创建测试用例 在 src/test/java 目录下创建测试用例,可以使用 Karate 提供的特定语法编写测试用例。例如,以下是一个测试 HTTP GET 请求的示例: ``` Feature: HTTP GET test Scenario: test GET Given url 'http://httpbin.org/get' When method GET Then status 200 And match response.headers['content-type'] contains 'application/json' ``` 6. 运行测试用例 在终端中进入项目根目录,执行如下命令运行测试用例: ``` mvn test ``` 执行完毕后,将在 target/surefire-reports 目录下生成测试报告。 以上就是 Karate 自动化测试框架的搭建步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值