使用 Camunda DMN JUnit 5 扩展在测试中注入 DMN 引擎:依赖配置、注入方式与自定义引擎配置实战
2026/9/18 20:48:45 网站建设 项目流程

使用 Camunda DMN JUnit 5 扩展在测试中注入 DMN 引擎:依赖配置、注入方式与自定义引擎配置实战

【免费下载链接】camunda-bpm-platformCamunda 7 CE is End of Life (EoL). Please check out Camunda 8 instead (https://github.com/camunda/camunda) or read about Camunda 7 Enterprise End of Life (https://camunda.com/blog/2025/02/camunda-7-enterprise-end-of-life-extension/) – Camunda 7 CE was a flexible framework for workflow and decision automation using BPMN and DMN.项目地址: https://gitcode.com/GitHub_Trending/ca/camunda-bpm-platform

<输出文章>

Camunda Platform DMN JUnit 5 扩展实战:在测试中注入 DMN 引擎的完整指南

本指南围绕 test-utils/junit5-extension-dmn/README.md 展开,系统讲解如何通过camunda-dmn-junit5这个 JUnit 5 扩展,在测试类中快速获得一个可用的DmnEngine实例。文章覆盖 Maven 依赖引入、@ExtendWith字段注入与方法参数注入两种使用姿势,并结合仓库源码剖析扩展的实现原理,最后介绍通过@RegisterExtension+forConfiguration使用自定义DmnEngineConfiguration的高级用法。读完本文,你可以在不依赖流程引擎、不编写样板代码的情况下,直接对 DMN 决策模型编写单元测试。

1. 扩展是什么:一句话定位

camunda-dmn-junit5是 Camunda Platform 提供的一个轻量级 JUnit 5 扩展(Extension),核心能力只有一件事:在 JUnit 5 测试中创建一个DmnEngine并注入到测试类字段或测试方法参数中。它与面向流程引擎的 camunda-bpm-junit5 定位互补——前者注入ProcessEngine,后者专门服务于DmnEngine,两者都可与camunda-engine-dmn(engine-dmn/engine)配合使用。

该扩展的实现只有一个类 DmnEngineExtension.java,同时实现了 JUnit 5 的TestInstancePostProcessorBeforeTestExecutionCallbackParameterResolver三个扩展接口,从而同时支持字段注入参数注入两条路径。

2. 快速上手

2.1 添加 Maven 依赖

pom.xml中加入如下依赖:

<dependency> <groupId>org.camunda.bpm.dmn</groupId> <artifactId>camunda-dmn-junit5</artifactId> <version>7.20.0</version> <scope>test</scope> </dependency>

要点说明:

  • <scope>test</scope>是必须的:该扩展只在测试编译与运行期使用,不应被打进生产制品。
  • 版本号请按你使用的 Camunda 发行版对齐。当前仓库对应版本为 7.24.0-SNAPSHOT(见 pom.xml 中的 parent 版本),README 示例中的7.20.0是可用的社区版坐标之一。
  • 扩展对camunda-engine-dmn与 JUnit Jupiter 均以provided作用域声明(见 pom.xml),这意味着你的测试工程需要自行引入camunda-engine-dmn(获得DmnEngine类型)和 JUnit 5(获得@ExtendWith等注解);工程自身则使用junit-bom统一管理 JUnit 5 版本(5.9.3,见 pom.xml)。
  • 如果仅想测试 DMN 引擎本身,可以直接引入 engine-dmn/engine 模块(org.camunda.bpm.dmn:camunda-engine-dmn),它自带DmnEngineDmnEngineConfiguration与测试用 DmnEngineRule。

2.2 编写第一个测试:@ExtendWith+ 字段注入

import org.camunda.bpm.dmn.engine.DmnEngine; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(DmnEngineExtension.class) class MyDmnTest { // 声明一个字段,扩展会自动注入 DmnEngine 实例 public DmnEngine dmnEngine; @Test void shouldEvaluateDecision() { // 直接使用注入的 dmnEngine 解析并求值决策 } }

2.3 方法参数注入:按需获取

如果不想声明字段,也可以把它作为测试方法参数,扩展会按类型匹配自动注入:

@Test void testDecision(DmnEngine dmnEngine) { // 使用 dmnEngine }

字段注入与参数注入可以混用,选择哪种取决于代码风格;仓库自带的测试 DmnEngineExtensionTest.java 就同时演示了两种方式(shouldInjectClassField用字段、shouldInjectMethodParameter用参数),并且两个测试共用同一个注入实例完成相同的决策求值断言。

3. 一个可以跑通的完整示例

参考 DmnEngineExtensionTest.java 与仓库内的 DMN 样例文件 DecisionWithLiteralExpression.dmn,一个完整可运行的测试如下:

package org.camunda.bpm.dmn.engine.test.junit5; import static org.assertj.core.api.Assertions.assertThat; import static org.camunda.bpm.engine.variable.Variables.createVariables; import org.camunda.bpm.dmn.engine.DmnDecisionResult; import org.camunda.bpm.dmn.engine.DmnEngine; import org.camunda.commons.utils.IoUtil; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @ExtendWith(DmnEngineExtension.class) class DmnEngineExtensionTest { public static final String DMN_MINIMAL = "org/camunda/bpm/dmn/engine/test/junit5/DecisionWithLiteralExpression.dmn"; // 字段注入 DmnEngine dmnEngineField; @Test void shouldInjectClassField() { shouldEvaluateDecisionWithLiteralExpression(dmnEngineField); } // 方法参数注入 @Test void shouldInjectMethodParameter(DmnEngine dmnEngineParam) { shouldEvaluateDecisionWithLiteralExpression(dmnEngineParam); } protected void shouldEvaluateDecisionWithLiteralExpression(DmnEngine dmnEngine) { DmnDecisionResult result = dmnEngine.evaluateDecision( dmnEngine.parseDecision("decision", IoUtil.fileAsStream(DMN_MINIMAL)), createVariables() .putValue("a", 2) .putValue("b", 3)); assertThat(result.getSingleResult()).containsOnlyKeys("c"); assertThat((int) result.getSingleEntry()).isEqualTo(5); } }

对应 DMN 决策模型(位于 engine-dmn/engine/src/test/resources/org/camunda/bpm/dmn/engine/api/DecisionWithLiteralExpression.dmn):

<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns="http://www.omg.org/spec/DMN/20151101/dmn.xsd" id="definitions" name="Definitions" namespace="http://camunda.org/schema/1.0/dmn"> <decision id="decision" name="Decision with Literal Expression"> <variable name="result" typeRef="string" /> <literalExpression> <text>input == "ok" ? "ok" : "notok"</text> </literalExpression> </decision> </definitions>

这个示例展示了使用注入的DmnEngine的典型三步流程:

  1. parseDecision("decision", stream):按决策id解析 DMN 模型,得到DmnDecisionDmnEngine接口定义见 DmnEngine.java);
  2. createVariables().putValue(...):通过 typed-values 模块的Variables工厂构建求值变量;
  3. evaluateDecision(...):执行求值并返回DmnDecisionResult,随后用 AssertJ 对结果做断言。

4. 注入机制原理:源码级解读

DmnEngineExtension的类声明如下(DmnEngineExtension.java):

public class DmnEngineExtension implements TestInstancePostProcessor, BeforeTestExecutionCallback, ParameterResolver {

三条注入路径分别对应三个接口回调:

  • 字段注入(实例字段)postProcessTestInstance在测试实例创建后被调用,通过反射扫描测试类中类型为DmnEngine的字段并赋值(injectIntoTestInstance使用getDeclaredFields()遍历,即使字段是private也能注入,因为injectDmnEngine会调用field.setAccessible(true))。
  • 字段注入(static 字段)beforeTestExecution在每个测试方法执行前再次执行同样的字段注入逻辑,因此即使DmnEngine字段是static的,也能在测试执行前被正确赋值——这由ExtensionContext.getTestInstance()拿到实例后注入实现。
  • 参数注入supportsParameter仅当参数类型严格等于DmnEngine.class时返回trueresolveParameter在类型匹配时初始化引擎并返回该实例。注意它是精确类型匹配,子类或接口类型不会被注入。

引擎创建采取惰性初始化initializeDmnEngine()只有在dmnEngine == null时才调用dmnEngineConfiguration.buildEngine()构建,因此同一个扩展实例在同一测试生命周期内复用一个引擎实例,不会为每个测试方法重复创建。

注入的类型判断可见 DmnEngineExtension.java(参数解析)与 DmnEngineExtension.java(字段注入)。

5. 高级用法:使用自定义 DMN 引擎配置

默认情况下,扩展使用DmnEngineConfiguration.createDefaultDmnEngineConfiguration()创建默认配置(DmnEngineConfiguration.java)。当测试需要自定义配置(例如注册自定义表达式语言、自定义DmnDecisionEvaluationListener、设置引擎指标收集器等)时,可以使用静态工厂方法forConfiguration配合@RegisterExtension

import org.camunda.bpm.dmn.engine.DmnEngineConfiguration; import org.camunda.bpm.dmn.engine.impl.DefaultDmnEngineConfiguration; import org.junit.jupiter.api.extension.RegisterExtension; class RegisterDmnEngineExtensionTest { private DmnEngineConfiguration customConfiguration = new DefaultDmnEngineConfiguration(); @RegisterExtension private DmnEngineExtension dmnEngineExtension = DmnEngineExtension.forConfiguration(customConfiguration); // 之后被注入的 DmnEngine 将基于 customConfiguration 构建 }

forConfiguration的实现(DmnEngineExtension.java)对空配置做了防御:

public static DmnEngineExtension forConfiguration(DmnEngineConfiguration configuration) { return new DmnEngineExtension( Objects.requireNonNull(configuration, "configuration must not be null")); }

对应测试 RegisterDmnEngineExtensionTest.java 验证了三点行为:

  • 传入null配置会抛出带信息configuration must not be nullNullPointerException
  • 注入到static字段、实例字段以及方法参数中的引擎,其getConfiguration()都与传入的自定义配置对象相同(assertThat(...getConfiguration()).isEqualTo(customConfiguration));
  • 三种注入方式(static 字段、实例字段、方法参数)行为一致。

需要注意:DmnEngineConfiguration的 Javadoc 明确指出,对配置实例的修改也会影响已由该配置构建的引擎(见 DmnEngineConfiguration.java),因此在测试中复用配置对象时要注意状态共享。

此外,DmnEngineConfiguration还提供了丰富的可配置项,例如引擎指标收集器setEngineMetricCollector、决策表求值监听器customPreDecisionTableEvaluationListeners等(见 DmnEngineConfiguration.java),这些都可以在测试中按需装配。

6. 与 DmnEngineRule(JUnit 4)的对比

如果你的项目仍在使用 JUnit 4,仓库同样提供了基于 Rule 的等效方案 DmnEngineRule.java。它与DmnEngineExtension的对应关系非常直接:DmnEngineRule也支持默认配置(构造时调用createDefaultDmnEngineConfiguration())与自定义配置两种构造方式,并在内部通过dmnEngineConfiguration.buildEngine()构建引擎(见 DmnEngineRule.java)。

从源码结构看,两个类共享同一套引擎构建与配置注入思路,只是接入点从 JUnit 4 的TestRule换成了 JUnit 5 的ExtensionAPI。对于新项目,推荐直接使用camunda-dmn-junit5

7. 版本与维护说明

依据 pom.xml 中模块描述:7.24.0 是发布到 Maven Central 的最后一个社区版,该库后续不再发布新版本。Camunda 7 社区版已进入 End of Life(EoL)阶段,项目 README(README.md)也明确提示用户转向 Camunda 8。因此本文介绍的能力基于当前仓库源码(7.24.0-SNAPSHOT 时代)验证,在迁移到 Camunda 8 时请以新平台的测试工具为准。

8. 小结

使用方式关键代码适用场景
默认引擎 + 字段注入@ExtendWith(DmnEngineExtension.class)+public DmnEngine dmnEngine;大多数 DMN 决策单测
默认引擎 + 参数注入@Test void test(DmnEngine dmnEngine) {...}只需在个别测试中使用引擎
自定义配置@RegisterExtension+DmnEngineExtension.forConfiguration(cfg)需要监听器、自定义 EL、指标收集器等
JUnit 4DmnEngineRule存量 JUnit 4 项目

camunda-dmn-junit5将「创建引擎 + 注入实例」的样板代码压缩为零:依赖坐标一行、类注解一行、字段或参数一行。配合仓库内的 DmnEngineExtensionTest.java 与 RegisterDmnEngineExtensionTest.java 两个测试用例,你可以直接作为模板开启 DMN 决策的单元测试之旅。

【免费下载链接】camunda-bpm-platformCamunda 7 CE is End of Life (EoL). Please check out Camunda 8 instead (https://github.com/camunda/camunda) or read about Camunda 7 Enterprise End of Life (https://camunda.com/blog/2025/02/camunda-7-enterprise-end-of-life-extension/) – Camunda 7 CE was a flexible framework for workflow and decision automation using BPMN and DMN.项目地址: https://gitcode.com/GitHub_Trending/ca/camunda-bpm-platform

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询