测试最佳实践

为 Actions on Google 平台开发 Action 时,通常需要实现 Dialogflow 以实现自然语言理解 (NLU) 和 Dialogflow fulfillment(用于处理 Action 的逻辑)。 在代码库中添加测试有助于确保您的 Action 在生产环境中按预期运行。

在为您的 Action 实现单元、集成或端到端测试时,您应该将 Dialogflow 代理和 fulfillment 视为单独的组件。

一个流程图从用户查询持续到 Actions on Google、Dialogflow 和 fulfillment webhook,最终返回到用户。

图 1. 描述要用于测试的系统的流程图

测试 Dialogflow 代理

Dialogflow 代理和 fulfillment 作为单独的组件进行测试。以下小节介绍了如何构思和测试 Action 的 Dialogflow 代理。

将 Dialogflow 作为查询输入和意图输出系统

您的 Dialogflow 代理负责获取用户的查询,将其与意图进行匹配,并从查询中提取所有预定义的实体。代理 (Agent) 通过传递包含匹配的 intent、其参数和 Actions on Google 元数据的消息与执行方式进行交互。

作为开发者,您可以控制 Dialogflow 代理的配置,例如意图和实体的结构。Actions on Google 元数据来自 Actions on Google,可以假定包含用于测试的正确数据。

测试时,请专注于让您的代理能够正确提取 intent 参数,以及将查询与 intent 匹配。此方法提供了一个用于评估代理性能的可量化指标。您可以通过准备和使用单个测试用例或验证集来计算此指标。

Dialogflow 代理可以用文本查询作为输入,将意图加上提取的 intent 参数作为输出。

图 2. 将 Dialogflow 表示为查询输入和意图输出系统

单元测试

对于 Dialogflow 代理,您可以编写测试,其中每个用例都期望将文本查询作为输入,并生成意图元数据作为输出。此元数据应(至少)包含匹配的 intent 的名称和匹配的参数列表。

Dialogflow API 的 detectIntent 端点将文本查询作为输入,并生成包含已解析 intent 的名称和所提取参数的结构化输出。此输出对于评估代理的意图匹配性能非常有用。如需查看其他实用字段的完整参考,请参阅 QueryResult 参考文档

示例测试如下所示:

it('choose_fact', async function() {
  // The `dialogflow` variable is an abstraction around the API that creates
  // and sends payloads to Dialogflow.
  const resJson = await dialogflow.detectIntent(
    'Tell me about the history of Google');
  expect(resJson.queryResult).to.include.deep.keys('parameters');
  // Check that Dialogflow extracted required entities from the query.
  expect(resJson.queryResult.parameters).to.deep.equal({
    'category': 'history',
    // Put any other parameters you wish were extracted
  });
  expect(resJson.queryResult.intent.displayName).to.equal('choose_fact');
});

此代码段使用 MochaChai。如需了解以 Node.js 编写的 Dialogflow 单元测试的完整工作示例,请参阅 Google 简介

由于 Dialogflow API 接受 sessionId 作为参数,因此您的测试文件可以并行运行。因此,在使用单个 Dialogflow API 客户端时,您可以为每个对话使用单独的沙盒。

由于您是向 Dialogflow API 发出请求,因此如果达到免费调用的配额,可能会产生费用。如需了解详情,请参阅配额和限制

集成测试

Dialogflow API 的 detectIntent 端点也会触发第三方执行方式。因此,您可以编写涵盖 Dialogflow 代理与 Dialogflow fulfillment 之间的集成的测试用例。

为 Dialogflow 编写集成测试与编写单元测试之间的主要区别在于,在集成测试中,您可以断言来自 webhook 的响应以及 Dialogflow 意图和实体提取。

如需查看以 Node.js 编写的集成测试的完整可运行示例,请参阅 Google 简介代码库。

测试 Dialogflow fulfillment webhook

Dialogflow 代理和 Dialogflow 执行方式作为单独的组件进行测试。以下小节介绍了如何构思和测试 Action 的执行方式。

执行方式作为 JSON 输入和 JSON 输出系统

您的 Dialogflow 执行方式代码同时接受请求,并以 JSON 格式生成响应。因此,您可以将执行方式代码视为 JSON 输入和 JSON 输出系统,以便对其进行测试。该请求包含来自 Dialogflow 和 Actions on Google 的元数据,因此它具有在执行方式中触发特定 intent 处理程序所需的全部信息。

如需测试 intent 处理程序的触发情况,您可以向 Action 发送 JSON 请求(输入)。此请求会传递给可在互联网上访问的执行方式。然后,执行方式会生成 JSON 响应(输出),可以对其进行评估以进行验证。

执行方式可用 JSON 请求输入和 webhook JSON 响应输出表示。

图 3. 将执行方式表示为 JSON 输入和 JSON 输出系统

单元测试

可以将执行方式 webhook 代码视为接受 JSON 输入并生成 JSON 输出的系统。然后,测试 Action 的过程会简化,以便向执行方式提供请求并检查生成的输出 JSON。

这样,您就可以随意在本地托管执行方式并在本地发送 HTTP 请求以进行测试。如果您使用的是 Actions on Google Node.js 客户端库,还可以将 JSON 请求直接发送到客户端库中间件层。

如果您使用 JSON 输入测试 webhook 代码并收到预期的 JSON 输出,则可以合理确信所控制的部分是否正常运行。您可以假设 Dialogflow 和 Actions on Google 运行正常,因为它们生成了正确的 JSON 载荷。这种隔离为编写测试提供了简化的编程模型。

下面简要介绍了测试流程:

  1. 使用 Actions 控制台中的模拟器获取用例中每个步骤的 JSON 请求。将其保存为 JSON 文件。或者,您可以使用 webhook 参考文档中的信息自行构建这些请求。
  2. 编写测试以使用这些载荷调用 webhook。
  3. 对于每个测试,请确保响应 JSON 包含预期商品。

此外,您还可以利用此模型在持续集成设置中测试 Dialogflow 执行方式,因为执行方式端点可以在本地运行,并且 Dialogflow API 内置了会话概念。

示例测试如下所示:

it('yes-history', function() {
  expect(jsonRes.payload).to.have.deep.keys('google');
  expect(jsonRes.payload.google.expectUserResponse).to.be.true;
  expect(jsonRes.payload.google.richResponse.items).to.have.lengthOf(3);
  expect(jsonRes.payload.google.richResponse.suggestions).to.have
    .deep.members([
      {'title': 'Sure'}, {'title': 'No thanks'},
    ]);
});

以上代码段使用了 MochaChai。如需查看使用 Node.js 编写的完整有效示例,请参阅关于 Google 的事实代码库。

设计可单元测试的执行方式

网络钩子代码通常包含自定义业务逻辑,您的应用需要利用这些逻辑来满足自身需求。此外,网络钩子代码还可以包含 intent 处理程序。

为了提高执行方式代码的单元测试的粒度,最好以这种方式组织代码,以使业务逻辑与 intent 处理例程分离。这意味着需要将 intent 处理程序和业务逻辑放在单独的模块中,以便单独测试每个部分。

如需查看示例,请参阅 GitHub 上的 Shiritori 示例操作。在该示例中,functions/index.jsfunctions/shiritori/*.js 分别包含 intent 处理程序和业务逻辑,因此能够实现更强大的测试套件。

集成测试

如需编写涵盖 Dialogflow 与 fulfillment webhook 代码集成的测试用例,请参阅上面的 Dialogflow 集成测试部分

加载测试

在将 Action 部署到生产环境之前,我们还建议对 webhook 执行方式进行负载测试,以发现导致执行方式服务降级或中断的性能问题。

下面是您在负载测试中可能会遇到的一些性能问题示例:

  • 计算和内存有限
  • 提供商的配额限制
  • 数据读写速度缓慢
  • 代码中的并发问题

负载测试场景取决于 Action 的预期或历史使用模式,但典型的测试场景是负载突然增加(高峰)和持续负载(浸泡)。

确定在哪些场景中调用 webhook,并执行资源密集型操作。典型的资源密集型操作包括查询数据库、调用另一个 API、执行计算和内存密集型操作(如渲染声音文件)。

在这些情况下,您可以从网络钩子日志或 Stackdriver 日志中捕获由 Actions on Google 服务器发送到网络钩子的请求。您也可以通过 Actions 控制台模拟器捕获请求。

收到请求后,您可以使用负载测试工具了解 webhook 在不同负载测试场景中如何响应。以下小节提供了使用 ApacheBench 的峰值测试和浸泡测试的一些示例。

高峰测试

高峰测试要求您向网络钩子发送一定数量的请求并持续增加负载。例如,您可以设置一项测试,测试的发送负载为每秒 10 次查询 (QPS),但有时会出现 60 次 QPS 的峰值。

您可以运行以下 ApacheBench 命令,将 60 个并发请求发送到您的网络钩子:

ab -n 60 -c 60 -p ActionRequest.json -T 'application/json' https://example.com/webhookFunctionName

假设 ActionRequest.json 文件包含发送到您的网络钩子的捕获的请求载荷。

浸泡测试

浸泡测试要求您向网络钩子发送恒定数量的请求并观察响应。例如,您可以设置一项测试,持续发送 10-20 QPS 的负载并持续几分钟,看看响应时间是否增加。

您可以运行以下 ApacheBench 命令来发送 1200 个请求,每秒 10 个并发请求:

ab -t 120 -n 1200 -p ActionRequest.json -T 'application/json' https://example.com/webhookFunctionName

假设 ActionRequest.json 文件包含发送到您的网络钩子的捕获的请求载荷。

分析负载测试结果

运行负载测试后,分析网络钩子响应时间的结果。webhook 实现中的问题指标通常是趋势,例如响应时间中位数随每次测试运行而增加,或者您的 Action 不可接受的最糟糕响应时间。

端到端测试

在提交 Action 以供审批之前,您可以使用 Actions 控制台模拟器进行端到端测试。您可以在 Actions 模拟器文档中找到通过 Actions 控制台模拟器进行端到端测试的步骤。执行这些测试有助于您消除 Actions on Google 基础架构组件的潜在不确定性。