我们一直遇到模板错误偶尔潜入我们的生产站点的问题。如果有一个工具来捕捉这些问题或有一个策略来提前捕捉这些问题,那么它可以被添加到交付管道中。
3 回答
根据我的经验,Freemarker 在尝试渲染模板时确实只有两类错误(忽略配置):
- 模板级别的简单语法错误
- 关于在 Java 级别传入的模型的假设是不正确的
尽管 lint 工具通常会发现代码中的错误,但 lint 工具并不能取代对基本测试的需求,这对于您在这里遇到的情况来说是一个更好的解决方案,因为您会在生产代码中看到异常。
无论好坏,Freemarker 关于谁在处理数据和谁在处理标记的假设是不同的人具有不同的技能。假设是这种情况(并且您有空闲的 Java 工程资源),从测试过程的角度来看,您有两种方法来解决这个问题(虽然要真正严格,但两者都需要)。
前端测试
在我之前的工作中,我们单独使用了这种方法。基本上,前端工程师会在一个特殊的 Web 前端破解模板,其中编辑的模板直接位于包含两个列表的配置路径上:
- 要渲染的模板
- 要呈现的数据模型的版本
“版本”本质上是两层硬编码 Java 对象集,其中第 1 层在所有模板中都是一致的,第 2 层是特定于模板的变体。因为我们的大多数电子邮件都是帐户级别的通知,所以我们仅仅拥有全局数据模型就获得了很多里程和重用,而且我们很少需要深入研究小东西。
好处
- 前端工程师永远不需要接触 Java,因此如果您的团队有这些,您可以将 HTML/CSS maven 用于他们的预期目的
- 后端工程师可以根据模板及其构造回收很多东西
- 在引用诸如“帐户”之类的常用值时,松散地强制统一数据模型变量名称(当他们期望的值不存在时,前端工程师会感到恼火,因为不同的后端工程师命名为“不正确”)
不太好
- 这基本上是手动测试,在某些时候会无法捕获错误
后端测试
另一种选择是在创建每个模板时对它们进行单元测试。因为 Freemarker 中的异常都是在编译时发生的,所以您只需要执行以下操作(在您进行初始设置之后):
Template temp = cfg.getTemplate("myTestedTemplate.ftl");
temp.process(myTestDataModel, myIgnoredOutput); // No exceptions -> OK for us
请注意,在这种情况下您不关心结果,您只需要编译它。这很重要,因为您很容易陷入调试 Java 编译输出的困境,而无法解决当前问题。和以前一样,您很可能还希望在这里进行相同的两层单元测试:
- 使用一些通用模型对所有模板进行烟雾测试(可以通过编程方式获取每个模板)
- 使用您期望的数据模型变化测试单个模板(缺少设置、不完整的字段等)
好处
- 构建/部署过程的一部分,因此您可以消除模板中的人为错误
- 根据您使用 Freemarker 的方式,如果您还没有这样做,这还可以验证其他进程是否正确生成了数据模型
不太好
- 前端工程师需要阅读堆栈跟踪以找出失败的原因
- 使用这种方法进行开发最终会与您的构建过程联系在一起,这远没有重新加载页面那么快
如果您有时间同时做这两件事,我建议您使用单元测试来处理您在弹出时发现的边缘情况和其他问题,然后使用 Web 前端进行开发,这样开发页面就不需要重新编译. 前端版本化的能力非常有益,但目标是首先防止生产中的错误到构建过程。启动,然后优化等等。
如果您的意思是运行时输出错误而不是模板语法错误,您可以使用一组(脆弱的)“期望得到”集成测试。这有多合适取决于您的模板的复杂性以及它们的动态程度。如果您只想要一些简单的“冒烟”测试,这可能是一个不错的解决方案。
对于每个模板,至少创建一个测试。使用具体/静态/预先指定的输入数据和具体/静态/预先指定的输出结果。此输出必须在任何更改后第一次手动生成和验证,但从那时起可以保存它并且可以编写测试脚本。如果模板提取了无法设置为固定输入的自己的数据(如日期等),则将其从预期输出中屏蔽或删除。每个自动化测试应该:
- 从每个模板的至少一组输入数据中生成“得到”输出。
- 从输出中屏蔽或删除不可预测的变量区域。
- 将此“得到”结果与保存的、预先验证的“预期”输出进行比较。
- 当这些不同时报告失败。
精确的输出相等是最容易实现并确保正确的。如果需要,每个模板有多个测试。我不会试图变得聪明,只是让计算机做无聊和重复的工作。我会忽略模板中需要在第一次通过时被屏蔽的部分(一些测试比没有测试要好)。稍后当您认为它可以提高可靠性以值得付出努力时(或为过去做错的任何事情)编写显式测试。
该解决方案有以下注意事项。
范围太大。对模板或数据模型中的任何内容的任何更改都可能需要更新测试。使用“diff”可能有助于手动验证并确定在数据模型和/或模板更改时如何修改测试。
代码拒绝和模块化会导致测试问题。使用良好的模块化代码,一次代码更改会影响所有模板中的数据,但静态测试需要在发生这种情况时独立更改和重新验证所有测试。解决这个问题没有什么可做的,所以你的代码越好、越模块化,这导致的工作就越多:(
复杂的模板很难测试。仅使用几组静态数据来获得良好的模板覆盖率可能会有问题。这可能意味着模板做了太多的“处理”,并没有真正被用作模板。无论如何,这可能不是一个好主意。
我不知道有什么工具可以为您执行此操作,但要捕获语法错误,您所要做的就是将所有模板文件提供给new Template("whatever", theTemplateFileReader);
. 不幸的是,您无法通过这种方式检测运行时错误,例如对不存在的变量/宏/导入的引用。为此,您可以调用Template.process
,但如果没有实际应用程序中的数据模型,它当然没有意义。