前台组件测试:Karate Netty使用教程

413 阅读6分钟

前台的组件测试:Karate Netty项目

在本教程中,看看用于在组件测试中设置模拟服务器的Karate Netty是如何在易用性和功能丰富性之间取得良好平衡的。

在我之前的文章中,我强调了组件测试的性质。他们的范围应该是一个单一的可执行文件,其中所有的外部依赖已经被可配置的测试替身所取代。丰富的前端应用程序是这种测试的良好候选者。他们与之交互的后端应该返回可预测的和容易配置的响应,以满足相关的测试场景。真正的后端通常不能胜任这一任务。因此,在这篇续篇中,我将更详细地看一下Karate Netty项目的情况。

比起仅仅使用实际的后台,建立一个模拟服务器一定是值得努力的。如果设置起来很麻烦,而且很难学习,那么更谨慎的做法可能是根本不使用mock,而是以端到端方式运行你的测试。但这只有在后端是轻量级的并且可以在本地机器上运行时才行得通。在最复杂的微服务环境中,这种情况几乎不存在了。

一个合理的愿望清单是:

  • 它必须容易和快速地设置和运行。
  • 配置应该有一个温和的学习曲线;人类可读的语法。
  • 然而,它也应该有足够的功能,以满足我们在测试场景中需要的所有请求/响应对。

Karate Netty的大多数功能都支持这些要求:

  • 你可以把模拟服务器作为一个库导入,并把它纳入 JUnit测试的生命周期中,或者作为一个独立的可执行的jar文件从命令行中导入。
  • 配置语法是基于Gherkin语言的,而不是像JSON、YAML或XML这样的分层标记语言。交互被定义为小黄瓜场景。这使得它非常具有可读性。
  • 它有一个丰富的功能集来定义请求和响应。你甚至可以使服务器有状态并嵌入自定义的JavaScript代码。不过,在这些功能上要简单些。你应该很少需要将业务逻辑复制到你的测试代码中,否则你最终会验证测试代码而不是生产代码。
  • 它是可扩展的,并促进重复使用。当项目增长时,你可以将单独的场景分割成多个文件,并在JSON文件中重复使用共同的请求和响应有效载荷。

手写的还是自动的?

大多数后端以OpenAPI这样的开放格式暴露他们的API,而且已经有很多工具可以从这样的规范中生成一个模拟服务器,比如WiremockPostman。使用KarateStudio(一种订阅服务),你可以从OpenAPI规范中创建特征文件,并节省一些输入。

尽管这给了你一个最低限度的工作后台,但响应还没有为测试目的进行优化。这是因为业务规则中隐含的相关成功/失败场景在规范中并不明确。对于每个端点,我们需要几个独立的输入和输出组合场景,只要它们与前端用户流有关。任何不会对流程产生不同影响的请求/响应替代方案都可以被安全地忽略掉。

然而,请注意,前端和后端逻辑可以有非常不同的要求。记住我们的在线抵押贷款申请工具。想象一下,在这个过程中的某个时刻,你被要求上传你的工作证明(验证甚至可能是由人执行的,这与此无关)。假设这个步骤可以产生六个不同的响应代码。其中一个构成拒绝。这个流程就终止了。另外两种情况要求你上传另一份文件,每种情况都有其特定的信息。最后,有三个成功场景,每个场景都提出了不同的临时借贷条件并继续流程。

现在,在后台的组件测试中,所有六个场景都应该被详尽地测试。但是对于前端来说,只有三个场景是需要测试的:拒绝、要求提供文件和成功场景。两个文档请求和三个成功场景只在文本内容上有区别,这些内容是在从后台收到时呈现给用户的。对于我们的测试目的,它可以是任何东西。

为了测试这三个流程,我们至少需要相等数量的测试文件来产生预期的响应。想象一下,在真正的后端上设置这样的文件会是多么的尴尬,这就是为什么我们常常不费力气。然而,在Karate中,你只需要三个PDF文件,分别命名为rejected,further_docs ,success。它们可以是空的:只有名称是相关的。这就是你如何配置它:

Gherkin

Scenario: pathMatches('/api/v1/offer') && requestParts['file'][0].filename == 'rejected.pdf'
* def response = {"code": "REJECTED", "message": "You request has been rejected"}

Scenario: pathMatches('/api/v1/offer') && requestParts['file'][0].filename == 'further_docs.pdf'
* def response = {"code": "FURTHER_DOCS", "message": "Please upload "}

Scenario: pathMatches('/api/v1/offer') && requestParts['file'][0].filename == 'success.pdf'
* def response = {"code": "SUCCESS", "message": "You may proceed"}

请查看前面链接的优秀文档。这个例子只是触及了可能的表面。我们想拦截对/api/v1/offer 的调用。如果该URL处理除POST 以外的其他方法,我们需要在相关场景中添加&& methodIs('POST')JavaScript语法中的requestParts 子句更为复杂,它对上传文件的属性做了详细的匹配,在一个请求中可能有多个文件,因此要有索引。

生命周期管理

为你的服务器从独立的Karate可执行文件中创建一个Docker容器是非常容易的。下载可执行的jar或zip文件,并将其放置在你的特征文件旁边,与下列Docker文件一起:

Dockerfile

FROM openjdk:17
WORKDIR /
COPY . .
EXPOSE 8080
CMD java -jar karate.jar -m mock.feature -m mock2.feature -p 8080

这将把特征文件和Karate jar复制到一个Java 17镜像中,并在8080端口上启动该服务。

你也可以在docker-compose的设置中,将其与运行中的Node服务器放在一起。如果你使用Cypress框架进行测试,该文件将看起来像这样:

Dockerfile

version: '3'
services:
  webapp:
    image: webapp
    environment:
      - NODE_ENV=docker
    links:
      - "backend:backend"
    depends_on:
      - backend
    ports:
      - "8080:8080"
  backend:
    image: karate
    ports:
      - "8090:8080"
  cypress:
    image: "cypress/included:9.5.3"
    links:
      - "webapp:webapp"

前端应用程序通过Docker网络中的主机名 "backend "指向模拟服务器。你将需要设置一个依赖于NODE_ENV 环境变量的配置,在这个例子中设置为docker

不仅仅是为了测试自动化

Karate的可读语法的另一个优势是在前端组件的开发过程中帮助你,而不仅仅是为了测试。它可以成为前端和后端之间强有力的交流工具,并且可以让你在实际的后端准备使用之前很长时间就针对一个稳定和有代表性的后端开发你的前端代码。