【问题标题】:How can I write unit tests for velocity templates?如何为速度模板编写单元测试?
【发布时间】:2026-01-09 16:25:01
【问题描述】:

这可能吗?

朋友们好。 我正在使用 AWS AppSync + DynamoDB 构建应用程序,并且我开始拥有大量 resolver mapping templates,所有这些都是使用 Apache Velocity 模板语言 (VTL) 编写的。

我开始担心的是,这些 vtl 文件对应用程序非常重要(因为它们定义了如何检索数据)并且其中一个可能造成严重破坏的错误。所以就像系统的任何关键部分一样......我想为他们编写一些自动化的单元测试。但我没有发现很多其他人这样做。

  1. 如果您使用 VTL(与 AppSync 或 API 网关),您如何测试它们?
  2. 甚至可以为速度模板编写自动化测试吗?
  3. 还是我走错了路,我应该只使用 Lambda 作为解析器?

提前致谢!

【问题讨论】:

  • 我不太明白你的问题,你有速度模板没问题,但你想测试什么?
  • AWS AppSync 上下文中的速度模板可以包含有关如何与 DynamoDB 交互的逻辑 - 要读取的内容、要应用的过滤器值、如何插入数据等 - 它基本上是在构建查询。
  • 这里有一个例子(虽然可能相当极端),它可以变得多么复杂:docs.aws.amazon.com/appsync/latest/devguide/…
  • 输出仍然是一个简单的文件吗?如果是这样,您可以编写您的测试,带有输入和预期的输出文件。您使用输入处理模板并将生成的文件与预期的文件进行比较
  • 作为单元测试是的,这就是我为邮件模板所做的事情

标签: unit-testing amazon-dynamodb aws-appsync vtl velocity-template-language


【解决方案1】:

我自己花了一段时间才弄清楚这一点,但我找到了一种为我的 VTL 请求和响应模板编写单元测试的好方法。我使用了amplify-appsync-simulator npm 包的 VelocityTemplate 类。到目前为止,我看到的唯一警告是您需要在 VTL 中使用 $context,模拟器 VTL 渲染器无法识别缩写的 $ctx。看看吧:

我的 VTL:

#set( $timeNow = $util.time.nowEpochMilliSeconds() )
{
    "version" : "2018-05-29",
    "operation" : "PutItem",
    "key": {
        "pk" : { "S" : "game#${util.autoId()}" },
        "sk" : { "S" : "meta#${timeNow}" }
    },
    "attributeValues" : {
        "players": { "L" : [
            { "M" : {   
                ## num and color added at start game             
                "player": $util.dynamodb.toDynamoDBJson($context.args.input.player)    
            }}                        
        ]},
        "createdBy": { "S": "${context.identity.sub}"},
        "gsipk": { "S": "${context.args.input.status}"},
        "gsisk": { "S": "${context.args.input.level}#${context.args.input.maxPlayers}"},
        "gsipk2": {"S": "game"},
        "turns": { "L": [] },
        "nextPlayerNum": { "N": 1 },
        "createdAt": { "N": ${timeNow} },
        "updatedAt": { "N": ${timeNow} }
    }
}

我的测试:

import { AmplifyAppSyncSimulator } from 'amplify-appsync-simulator'
import { VelocityTemplate } from "amplify-appsync-simulator/lib/velocity"
import { readFileSync } from 'fs'
import path from 'path';

const vtl = readFileSync(path.join(__dirname, '..', 'addGame-request-mapping-template.vtl'))
const template = {
  content: vtl
}
const velocity = new VelocityTemplate(template, new AmplifyAppSyncSimulator)

describe('valid user and request', () => {

  // This is the graphql input
  const validContext = {
    arguments: {
      input: {
        player: 'player#1234',
        maxPlayers: 4,
        status: 'new',
        level: 7
      }
    },
    source: {}
  }

  // This is a logged in user with a JWT
  const requestContext = {
    requestAuthorizationMode: 'OPENID_CONNECT',
    jwt: {
      sub: 'abcd1234'
    }
  }

  const info = {
    fieldNodes: []
  }

  it('works', () => {
    const result = velocity.render(validContext, requestContext, info)
    expect(result).toEqual({
      result: {
        version: "2018-05-29",
        operation: "PutItem",
        key: {
          pk: { S: expect.stringMatching(/^game#[a-f0-9-]*$/) },
          sk: { S: expect.stringMatching(/^meta#[0-9]*$/)}
        },
        attributeValues: {
          players: {
            L: [
              { M: { player: { S: validContext.arguments.input.player }}}
            ]
          },
          createdBy: { S: requestContext.jwt.sub },
          gsipk: { S: validContext.arguments.input.status },
          gsisk: { S: `${validContext.arguments.input.level}#${validContext.arguments.input.maxPlayers}`},
          gsipk2: { S: 'game' },
          turns: { L: [] },
          nextPlayerNum: { N: 1 },
          createdAt: { N: expect.any(Number) },
          updatedAt: { N: expect.any(Number) }
        }
      },
      stash: {},
      errors: [],
      isReturn: false
    })
  })
})

【讨论】:

    【解决方案2】:

    发现这个项目https://github.com/ToQoz/api-gateway-mapping-template有点旧但仍然有效。

    它旨在测试 API Gateway 映射模板,因此它缺少您使用 AppSync 解析器获得的所有特殊 $util 功能,但我认为可以逐步添加缺少的实用程序。

    【讨论】:

      【解决方案3】:

      Amplify 刚刚发布了本地测试 AppSync api 的功能,包括 VTL 解析器。您可以查看他们的博客文章 https://aws.amazon.com/blogs/aws/new-local-mocking-and-testing-with-the-amplify-cli/,其中包含本地 API 模拟功能的操作方法;查找“当我编辑 VTL 模板时,Amplify CLI 会立即识别出来,并为解析器生成更新的代码”的位置。然后,您可以将其构建到您选择的 CI 或其他测试管道中。

      【讨论】: