All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.test.junit5.ProcessEngineExtension Maven / Gradle / Ivy

/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.camunda.bpm.engine.test.junit5;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.Supplier;

import org.camunda.bpm.engine.AuthorizationService;
import org.camunda.bpm.engine.CaseService;
import org.camunda.bpm.engine.DecisionService;
import org.camunda.bpm.engine.ExternalTaskService;
import org.camunda.bpm.engine.FilterService;
import org.camunda.bpm.engine.FormService;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.IdentityService;
import org.camunda.bpm.engine.ManagementService;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.ProcessEngineServices;
import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.impl.ProcessEngineImpl;
import org.camunda.bpm.engine.impl.ProcessEngineLogger;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.diagnostics.PlatformDiagnosticsRegistry;
import org.camunda.bpm.engine.impl.test.TestHelper;
import org.camunda.bpm.engine.impl.util.ClockUtil;
import org.camunda.bpm.engine.test.Deployment;
import org.camunda.bpm.engine.test.RequiredHistoryLevel;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestInstancePostProcessor;
import org.junit.jupiter.api.extension.TestWatcher;
import org.slf4j.Logger;

/**
 * Junit 5 Extension to create and inject a {@link ProcessEngine} into the test class.
 * 

* If you provide a {@code camunda.cfg.xml} file on the classpath. This file is used to configure the process engine. *
* Usage: *

*
 * @ExtendWith(ProcessEngineExtension.class)
 * public class YourTest {
 *
 *   // provide a property where the extension can inject the process engine
 *   public ProcessEngine processEngine;
 *
 *   ...
 * }
 * 
* *

* If you want to choose the {@code camunda.cfg.xml} file that is used in the test programmatically, * you can register the extension directly and use the builder pattern to configure it. *
* Usage with configuration: * *

* Usage: *

*

 * @RegisterExtension
 * ProcessEngineExtension extension = ProcessEngineExtension.builder()
 *    .configurationResource("myConfigurationFile.xml")
 *    .build();}
 * 
*

*

* You can declare a deployment with the {@link Deployment} annotation. This * base class will make sure that this deployment gets deployed before the setUp * and {@link RepositoryService#deleteDeployment(String, boolean) cascade * deleted} after the tearDown. *

*

* If you need the history service for your tests then you can specify the * required history level of the test method or class, using the * {@link RequiredHistoryLevel} annotation. If the current history level of the * process engine is lower than the specified one then the test is skipped. *

*/ public class ProcessEngineExtension implements TestWatcher, TestInstancePostProcessor, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterAllCallback, ParameterResolver, ProcessEngineServices { protected static final Logger LOG = ProcessEngineLogger.TEST_LOGGER.getLogger(); protected ProcessEngine processEngine; protected ProcessEngineConfigurationImpl processEngineConfiguration; protected RepositoryService repositoryService; protected RuntimeService runtimeService; protected TaskService taskService; protected HistoryService historyService; protected IdentityService identityService; protected ManagementService managementService; protected FormService formService; protected FilterService filterService; protected AuthorizationService authorizationService; protected CaseService caseService; protected ExternalTaskService externalTaskService; protected DecisionService decisionService; protected String configurationResource = "camunda.cfg.xml"; protected String deploymentId; protected boolean ensureCleanAfterTest = false; protected List additionalDeployments = new ArrayList<>(); // SETUP protected void initializeProcessEngine() { processEngine = TestHelper.getProcessEngine(configurationResource); processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration(); } protected void initializeServices() { processEngineConfiguration = ((ProcessEngineImpl) processEngine).getProcessEngineConfiguration(); repositoryService = processEngine.getRepositoryService(); runtimeService = processEngine.getRuntimeService(); taskService = processEngine.getTaskService(); historyService = processEngine.getHistoryService(); identityService = processEngine.getIdentityService(); managementService = processEngine.getManagementService(); formService = processEngine.getFormService(); authorizationService = processEngine.getAuthorizationService(); caseService = processEngine.getCaseService(); filterService = processEngine.getFilterService(); externalTaskService = processEngine.getExternalTaskService(); decisionService = processEngine.getDecisionService(); } protected void clearServiceReferences() { processEngineConfiguration = null; repositoryService = null; runtimeService = null; taskService = null; formService = null; historyService = null; identityService = null; managementService = null; authorizationService = null; caseService = null; filterService = null; externalTaskService = null; decisionService = null; } // TEST EXECUTION @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return parameterContext.getParameter().getType().equals(ProcessEngine.class); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { if (ProcessEngine.class.equals(parameterContext.getParameter().getType())) { LOG.debug("resolve the processEngine as parameter"); return getProcessEngine(); } else { return null; } } @Override public void beforeTestExecution(ExtensionContext context) { LOG.debug("beforeTestExecution: {}", context.getDisplayName()); final Method testMethod = context.getTestMethod().orElseThrow(illegalStateException("testMethod not set")); final Class testClass = context.getTestClass().orElseThrow(illegalStateException("testClass not set")); deploymentId = TestHelper.annotationDeploymentSetUp(processEngine, testClass, testMethod.getName(), null, testMethod.getParameterTypes()); boolean hasRequiredHistoryLevel = TestHelper.annotationRequiredHistoryLevelCheck(processEngine, testClass, testMethod.getName(), testMethod.getParameterTypes()); boolean hasRequiredDatabase = TestHelper.annotationRequiredDatabaseCheck(processEngine, testClass, testMethod.getName(), testMethod.getParameterTypes()); Assumptions.assumeTrue(hasRequiredHistoryLevel, "ignored because the current history level is too low"); Assumptions.assumeTrue(hasRequiredDatabase, "ignored because the database doesn't match the required ones"); } @Override public void afterTestExecution(ExtensionContext context) { final String testMethod = context.getTestMethod().orElseThrow(illegalStateException("testMethod not set")).getName(); final Class testClass = context.getTestClass().orElseThrow(illegalStateException("testClass not set")); TestHelper.annotationDeploymentTearDown(processEngine, deploymentId, testClass, testMethod); deploymentId = null; for (String additionalDeployment : additionalDeployments) { TestHelper.deleteDeployment(processEngine, additionalDeployment); } additionalDeployments.clear(); TestHelper.resetIdGenerator(processEngineConfiguration); ClockUtil.reset(); PlatformDiagnosticsRegistry.clear(); // finally clear database and fail test if database is dirty if (ensureCleanAfterTest) { TestHelper.assertAndEnsureCleanDbAndCache(processEngine); } } @Override public void afterAll(ExtensionContext context) throws Exception { clearServiceReferences(); } @Override public void postProcessTestInstance(Object testInstance, ExtensionContext context) { if (processEngine == null) { initializeProcessEngine(); } Arrays.stream(testInstance.getClass().getDeclaredFields()) .filter(field -> field.getType() == ProcessEngine.class) .forEach(field -> inject(testInstance, field)); } // FLUENT BUILDER public static ProcessEngineExtension builder() { return new ProcessEngineExtension(); } public ProcessEngineExtension configurationResource(String configurationResource) { this.configurationResource = configurationResource; return this; } public ProcessEngineExtension useProcessEngine(ProcessEngine engine) { this.setProcessEngine(engine); return this; } public ProcessEngineExtension ensureCleanAfterTest(boolean ensureCleanAfterTest) { this.ensureCleanAfterTest = ensureCleanAfterTest; return this; } public ProcessEngineExtension manageDeployment(org.camunda.bpm.engine.repository.Deployment deployment) { this.additionalDeployments.add(deployment.getId()); return this; } public ProcessEngineExtension build() { if (processEngine == null) { initializeProcessEngine(); } initializeServices(); return this; } // HELPER protected Supplier illegalStateException(String msg) { return () -> new IllegalStateException(msg); } protected void inject(Object instance, Field field) { field.setAccessible(true); try { field.set(instance, processEngine); } catch (IllegalAccessException iae) { throw new RuntimeException(iae); } } // GETTER / SETTER public void setCurrentTime(Date currentTime) { ClockUtil.setCurrentTime(currentTime); } public ProcessEngine getProcessEngine() { return processEngine; } public void setProcessEngine(ProcessEngine processEngine) { this.processEngine = processEngine; initializeServices(); } public String getConfigurationResource() { return configurationResource; } public ProcessEngineConfigurationImpl getProcessEngineConfiguration() { return processEngineConfiguration; } @Override public RepositoryService getRepositoryService() { return repositoryService; } public void setRepositoryService(RepositoryService repositoryService) { this.repositoryService = repositoryService; } @Override public RuntimeService getRuntimeService() { return runtimeService; } public void setRuntimeService(RuntimeService runtimeService) { this.runtimeService = runtimeService; } @Override public TaskService getTaskService() { return taskService; } public void setTaskService(TaskService taskService) { this.taskService = taskService; } @Override public HistoryService getHistoryService() { return historyService; } public void setHistoryService(HistoryService historyService) { this.historyService = historyService; } @Override public IdentityService getIdentityService() { return identityService; } public void setIdentityService(IdentityService identityService) { this.identityService = identityService; } @Override public ManagementService getManagementService() { return managementService; } public void setManagementService(ManagementService managementService) { this.managementService = managementService; } @Override public FormService getFormService() { return formService; } public void setFormService(FormService formService) { this.formService = formService; } @Override public FilterService getFilterService() { return filterService; } public void setFilterService(FilterService filterService) { this.filterService = filterService; } @Override public AuthorizationService getAuthorizationService() { return authorizationService; } public void setAuthorizationService(AuthorizationService authorizationService) { this.authorizationService = authorizationService; } @Override public CaseService getCaseService() { return caseService; } public void setCaseService(CaseService caseService) { this.caseService = caseService; } @Override public ExternalTaskService getExternalTaskService() { return externalTaskService; } public void setExternalTaskService(ExternalTaskService externalTaskService) { this.externalTaskService = externalTaskService; } @Override public DecisionService getDecisionService() { return decisionService; } public void setDecisionService(DecisionService decisionService) { this.decisionService = decisionService; } public void setProcessEngineConfiguration(ProcessEngineConfigurationImpl processEngineConfiguration) { this.processEngineConfiguration = processEngineConfiguration; } public void setConfigurationResource(String configurationResource) { this.configurationResource = configurationResource; } public String getDeploymentId() { return deploymentId; } public boolean isEnsureCleanAfterTest() { return ensureCleanAfterTest; } public void setEnsureCleanAfterTest(boolean ensureCleanAfterTest) { this.ensureCleanAfterTest = ensureCleanAfterTest; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy