使用 MockitoJUnitRunner 和 SpringRunner 进行 camunda 单元测试

Posted

技术标签:

【中文标题】使用 MockitoJUnitRunner 和 SpringRunner 进行 camunda 单元测试【英文标题】:camunda unit testing with MockitoJUnitRunner and SpringRunner 【发布时间】:2022-01-18 19:56:37 【问题描述】:

我正在为 Camunda 工作流程编写测试用例。我正在使用 SpringRunner @RunWith(SpringRunner.class) 并在测试执行所需的测试类中具有以下属性

        @Autowired
        private ProcessEngine processEngine;
    
        @Rule
        @ClassRule
        public static ProcessEngineRule rule;
    
        @PostConstruct
        void initRule() 
            rule = TestCoverageProcessEngineRuleBuilder.create(processEngine).withDetailedCoverageLogging().build();
        
        @Mock
        ProcessScenario someProcessScenario;

此外,在每个测试中,我都会像这样实例化 ProcessInstance

ProcessRunner.ExecutableRunner.StartingByStarter starter = Scenario.run(someProcessScenario)
            .startBy(() -> 
                processInstance = rule.getRuntimeService().startProcessInstanceByKey("PROCESS_DEFINITION", properties);
                return processInstance;
            );

starter.engine(rule.getProcessEngine());

此配置工作正常,我使用 BpmnAwareTests 断言并且所有测试都通过了。我在 pom 中使用的依赖项是

<dependency>
        <groupId>org.camunda.bpm.assert</groupId>
        <artifactId>camunda-bpm-assert</artifactId>
        <version>5.0.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.camunda.bpm.extension</groupId>
        <artifactId>camunda-bpm-assert-scenario</artifactId>
        <version>1.1.1</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.camunda.bpm.extension</groupId>
        <artifactId>camunda-bpm-process-test-coverage</artifactId>
        <version>0.3.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.camunda.bpm.extension.mockito</groupId>
        <artifactId>camunda-bpm-mockito</artifactId>
        <scope>test</scope>
        <version>4.12.0</version>
    </dependency>

由于这个设置为每个测试类实例化了 spring 容器,我想改变几个类来运行 MockitoJUnitRunner 而不是 SpringRunner。 所以我把那些改成@RunWith(MockitoJUnitRunner.class)

并像这样初始化所需的属性:

@Rule
public ProcessEngineRule rule = new ProcessEngineRule();

@Mock
ApplicationEventPublisher eventPublisher;

@Mock
ProcessScenario someOtherProcess;

@Mock
SomeClass someclass;


@Before
public void setUp() throws MyCustomiException 
    MockitoAnnotations.openMocks(this);
    MyDelegate myDelegate = new MyDelegate(someclass);
    Mocks.register("myDelegate", myDelegate);
    ......

ProcessInstance 在上面的所有测试用例中都被实例化。 这些测试也可以顺利运行并独立通过。 但是,当我运行所有测试(一些使用 SpringRunner 和其他使用 MockitoJUnitRunner 运行)时,它们没有通过。 SpringRunner 的所有测试都失败了,在 SpringRunner 之后执行的测试也失败了。 错误是 java.lang.IllegalStateException: No ProcessEngine found to be registered with ProcessEngines!

【问题讨论】:

【参考方案1】:

您混淆了两个测试概念:通过 spring 的集成测试和通过规则的单元测试。 Spring Boot 测试将根据您的 yaml 和 bean 配置配置流程引擎,并且(通常)运行大部分应用程序(数据库、消息传递......)。 使用该规则的单元测试在使用内存引擎(使用内存数据库)时效果最佳。

总结:在你的spring boot测试中,不要使用ProcessEngineRule,使用你的app配置的引擎。在你的纯流程单元测试中,不要使用弹簧,通过 MockExpressionManager 连接所有内容。

【讨论】:

【参考方案2】:

我发现进行单元测试的一个好方法是使用“隐藏的”引擎配置,当您只有一个引擎规则而没有其他基于弹簧的上下文时,它总是会被查找。 这可以通过简单地将 camunda.cfg.xml 添加到您的测试资源来完成,如下所示:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="processEngineConfiguration" class="org.camunda.bpm.extension.process_test_coverage.junit.rules.ProcessCoverageInMemProcessEngineConfiguration">

    <property name="jdbcUrl" value="jdbc:h2:mem:camunda;DB_CLOSE_DELAY=1000" />
    <property name="jdbcDriver" value="org.h2.Driver" />
    <property name="jdbcUsername" value="sa" />
    <property name="jdbcPassword" value="" />

    <!-- Database configurations -->
    <property name="databaseSchemaUpdate" value="true" />

    <!-- job executor configurations -->
    <property name="jobExecutorActivate" value="false" />

    <property name="history" value="full" />    
  </bean>
</beans>

请注意,这使用了流程覆盖引擎配置,您可以查找 here。但您可以使用任何其他配置。

一旦你有了这个引擎配置,你就可以像这样简单地创建一个单元测试:

@Deployment(resources = "myBpmFile.bpmn")
public class ApplicationTest 

    private static final String PROCESS_DEFINITION_KEY = "myProcessDefinitionKey";

    @Rule
    @ClassRule
    public static ProcessEngineRule rule = TestCoverageProcessEngineRuleBuilder.create().build();

    private RuntimeService runtimeService;

    @Before
    public void setup() 
        runtimeService = rule.getRuntimeService();

        registerJavaDelegateMock(MyDelegate.class)
        //register more mocks
    

    @Test
    public void testHappyPath() 
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(PROCESS_DEFINITION_KEY);
        assertThat(processInstance).isActive();

        assertThat(processInstance).hasPassed("MyServiceTask");
        verifyJavaDelegateMock(MyDelegate.class).executed();

        assertThat(processInstance).isWaitingAtExactly("MyUserTask");
        complete(task());

        assertThat(processInstance).isEnded();
    


请注意,这里使用camunda-bpm-mockito 和camunda-bpm-assert。 绝对值得研究一下,因为它使您的测试轻量级、快速、定义明确且易于阅读。

作为额外的奖励,测试覆盖率 EngineRule 将为您提供有价值的覆盖率图表(html 格式)并允许基于百分比的覆盖率断言。

编辑:这是针对 JUnit4 的。您也可以使用 JUnit 5 来​​做到这一点。

【讨论】:

以上是关于使用 MockitoJUnitRunner 和 SpringRunner 进行 camunda 单元测试的主要内容,如果未能解决你的问题,请参考以下文章

PowerMockRunner和ActiveObjectsJUnitRunner

如何在 JUnit5 中使用 Mockito

为啥我在使用 Mock 注释时有 NPE

[嘲笑QueryBuilder时为NullPointerException

初始化模拟对象 - MockIto

使用 Mockito 模拟服务器