diff --git a/modules/flowable-assertj/pom.xml b/modules/flowable-assertj/pom.xml
new file mode 100644
index 00000000000..5fb3762fa71
--- /dev/null
+++ b/modules/flowable-assertj/pom.xml
@@ -0,0 +1,42 @@
+
+
+ 4.0.0
+
+ org.flowable
+ flowable-root
+ ../..
+ 7.1.0-SNAPSHOT
+
+
+ flowable-assertj
+
+
+
+ org.assertj
+ assertj-core
+ compile
+
+
+ org.flowable
+ flowable-engine
+ provided
+
+
+ com.zaxxer
+ HikariCP
+ test
+
+
+ com.h2database
+ h2
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
\ No newline at end of file
diff --git a/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/FlowableProcessAssertions.java b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/FlowableProcessAssertions.java
new file mode 100644
index 00000000000..866fe74a99f
--- /dev/null
+++ b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/FlowableProcessAssertions.java
@@ -0,0 +1,32 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.assertj.core.api.Assertions;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+
+/**
+ * @author martin.grofcik
+ */
+public class FlowableProcessAssertions extends Assertions {
+
+ public static ProcessInstanceAssert assertThat(ProcessInstance processInstance) {
+ return new ProcessInstanceAssert(processInstance);
+ }
+ public static HistoricProcessInstanceAssert assertThat(HistoricProcessInstance historicProcessInstance) {
+ return new HistoricProcessInstanceAssert(historicProcessInstance);
+ }
+
+}
diff --git a/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/HistoricProcessInstanceAssert.java b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/HistoricProcessInstanceAssert.java
new file mode 100644
index 00000000000..d59bcd5b376
--- /dev/null
+++ b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/HistoricProcessInstanceAssert.java
@@ -0,0 +1,160 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.api.ListAssert;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.identitylink.api.IdentityLink;
+import org.flowable.identitylink.api.history.HistoricIdentityLink;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.flowable.variable.api.history.HistoricVariableInstance;
+
+/**
+ * @author martin.grofcik
+ */
+public class HistoricProcessInstanceAssert extends AbstractAssert {
+
+ protected final ProcessServicesProvider processServicesProvider;
+
+ protected HistoricProcessInstanceAssert(ProcessEngine processEngine, HistoricProcessInstance historicProcessInstance) {
+ super(historicProcessInstance, HistoricProcessInstanceAssert.class);
+ processServicesProvider = ProcessServicesProvider.of(processEngine);
+ }
+
+ protected HistoricProcessInstanceAssert(HistoricProcessInstance historicProcessInstance) {
+ this(Utils.getProcessEngine(), historicProcessInstance);
+ }
+
+ /**
+ * Assert historic activities ordered by activity instance start time.
+ *
+ * @return Assertion of {@link HistoricActivityInstance} list.
+ */
+ public ListAssert activities() {
+ processExistsInHistory();
+
+ return assertThat(processServicesProvider.getHistoryService().createHistoricActivityInstanceQuery().processInstanceId(actual.getId())
+ .orderByHistoricActivityInstanceStartTime().desc().list());
+ }
+
+ /**
+ * Assert historic process instance exists in the history and is finished.
+ *
+ * @return Historic process instance assertion.
+ */
+ public HistoricProcessInstanceAssert isFinished() {
+ processExistsInHistory();
+
+ if (processServicesProvider.getHistoryService().createHistoricProcessInstanceQuery().finished().processInstanceId(actual.getId()).count() != 1) {
+ failWithMessage(Utils.getProcessDescription(actual) + " to be finished, but is running in history.");
+ }
+
+ return this;
+ }
+
+ public ListAssert variables() {
+ processExistsInHistory();
+
+ return assertThat(processServicesProvider.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(actual.getId()).orderByVariableName().asc().list());
+ }
+
+ /**
+ * Assert that process instance has variable in history.
+ *
+ * @param variableName variable to check.
+ * @return Historic process instance assertion
+ */
+
+ public HistoricProcessInstanceAssert hasVariable(String variableName) {
+ processExistsInHistory();
+
+ if (processServicesProvider.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(actual.getId()).variableExists(variableName).count() != 1) {
+ failWithMessage(Utils.getProcessDescription(actual) + " has variable <%s> but variable does not exist in history.", variableName);
+ }
+
+ return this;
+ }
+
+ /**
+ * Assert that process instance does not have variable in history.
+ * @param variableName variable to check
+ * @return Historic process instance assertion
+ */
+ public HistoricProcessInstanceAssert doesNotHaveVariable(String variableName) {
+ processExistsInHistory();
+
+ if (processServicesProvider.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(actual.getId()).variableExists(variableName).count() != 0) {
+ failWithMessage(Utils.getProcessDescription(actual)+" does not have variable <%s> but variable exists in history.", variableName);
+ }
+
+ return this;
+ }
+
+ /**
+ * Assert that process instance has variable in history with value equals to expectedValue.
+ *
+ * @param variableName variable to check.
+ * @param expectedValue expected variable value.
+ * @return Historic process instance assertion
+ */
+ public HistoricProcessInstanceAssert hasVariableWithValue(String variableName, Object expectedValue) {
+ processExistsInHistory();
+ hasVariable(variableName);
+
+ HistoricVariableInstance actualVariable = processServicesProvider.getHistoryService().createHistoricVariableInstanceQuery().processInstanceId(actual.getId()).variableName(variableName).singleResult();
+ assertThat(actualVariable.getValue()).isEqualTo(expectedValue);
+ return this;
+ }
+
+ /**
+ * Assert list of historic identity links without ordering.
+ *
+ * @return Assertion of #{@link IdentityLink} list.
+ */
+ public ListAssert identityLinks() {
+ processExistsInHistory();
+
+ return assertThat(processServicesProvider.getHistoryService().getHistoricIdentityLinksForProcessInstance(actual.getId()));
+ }
+
+ /**
+ * Assert list of user tasks in the history ordered by the task name ascending.
+ * Process, Task variables and identityLinks are included.
+ *
+ * @return Assertion of {@link HistoricTaskInstance} list.
+ */
+ public ListAssert userTasks() {
+ processExistsInHistory();
+
+ return assertThat(processServicesProvider.getHistoryService().createHistoricTaskInstanceQuery().processInstanceId(actual.getId()).orderByTaskName().asc()
+ .includeProcessVariables().includeIdentityLinks().includeTaskLocalVariables().list());
+ }
+
+ private void processExistsInHistory() {
+ isNotNull();
+ isInHistory();
+ }
+
+ private void isInHistory() {
+ if (processServicesProvider.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(actual.getId()).count() != 1) {
+ failWithMessage(Utils.getProcessDescription(actual)+"> exists in history but process instance not found.");
+ }
+ }
+
+}
diff --git a/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessInstanceAssert.java b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessInstanceAssert.java
new file mode 100644
index 00000000000..b134eb071ba
--- /dev/null
+++ b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessInstanceAssert.java
@@ -0,0 +1,195 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.assertj.core.api.AbstractAssert;
+import org.assertj.core.api.Assertions;
+import org.assertj.core.api.ListAssert;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.engine.runtime.ActivityInstance;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.eventsubscription.api.EventSubscription;
+import org.flowable.identitylink.api.IdentityLink;
+import org.flowable.task.api.Task;
+import org.flowable.variable.api.persistence.entity.VariableInstance;
+
+import static org.flowable.assertj.process.FlowableProcessAssertions.assertThat;
+
+/**
+ * @author martin.grofcik
+ */
+public class ProcessInstanceAssert extends AbstractAssert {
+ protected final ProcessServicesProvider processServicesProvider;
+
+ protected ProcessInstanceAssert(ProcessEngine processEngine, ProcessInstance processInstance) {
+ super(processInstance, ProcessInstanceAssert.class);
+ processServicesProvider = ProcessServicesProvider.of(processEngine);
+ }
+
+ protected ProcessInstanceAssert(ProcessInstance processInstance) {
+ this(Utils.getProcessEngine(), processInstance);
+ }
+
+ /**
+ * Assert that process instance exists in runtime.
+ *
+ * @return Process instance assert.
+ */
+ public ProcessInstanceAssert isRunning() {
+ isNotNull();
+
+ if (processServicesProvider.getRuntimeService().createProcessInstanceQuery().processInstanceId(actual.getId()).count() < 1) {
+ failWithMessage(Utils.getProcessDescription(actual)+" to be running but is not.", actual.getId());
+ }
+ return this;
+ }
+
+ /**
+ * Assert that process instance has variable in runtime.
+ *
+ * @param variableName variable to check.
+ * @return Process instance assertion
+ */
+ public ProcessInstanceAssert hasVariable(String variableName) {
+ isNotNull();
+
+ if (processServicesProvider.getRuntimeService().createProcessInstanceQuery().processInstanceId(actual.getId()).variableExists(variableName).count() != 1) {
+ failWithMessage(Utils.getProcessDescription(actual)+" has variable <%s> but variable does not exist.", variableName);
+ }
+
+ return this;
+ }
+
+ /**
+ * Assert that process instance has variable in runtime with value equals to expectedValue.
+ *
+ * @param variableName variable to check.
+ * @param expectedValue expected variable value.
+ * @return Process instance assertion
+ */
+ public ProcessInstanceAssert hasVariableWithValue(String variableName, Object expectedValue) {
+ isNotNull();
+ hasVariable(variableName);
+
+ VariableInstance actualVariable = processServicesProvider.getRuntimeService().createVariableInstanceQuery().processInstanceId(actual.getId()).variableName(variableName).singleResult();
+ Assertions.assertThat(actualVariable.getValue()).isEqualTo(expectedValue);
+ return this;
+ }
+
+ /**
+ * Assert that process instance does not have variable in runtime.
+ * @param variableName variable to check
+ * @return Process instance assertion
+ */
+ public ProcessInstanceAssert doesNotHaveVariable(String variableName) {
+ isNotNull();
+
+ if (processServicesProvider.getRuntimeService().createProcessInstanceQuery().processInstanceId(actual.getId()).variableExists(variableName).count() != 0) {
+ failWithMessage(Utils.getProcessDescription(actual)+" does not have variable <%s> but variable exists.", variableName);
+ }
+
+ return this;
+ }
+
+ /**
+ * Assert that process instance does not exist in runtime.
+ *
+ * @return Process instance assertion
+ */
+ public ProcessInstanceAssert doesNotExist() {
+ isNotNull();
+
+ if (processServicesProvider.getRuntimeService().createProcessInstanceQuery().processInstanceId(actual.getId()).count() != 0) {
+ failWithMessage(Utils.getProcessDescription(actual)+" is finished but instance exists in runtime.");
+ }
+
+ return this;
+ }
+
+ /**
+ * @return Historic process instance assertion
+ */
+ public HistoricProcessInstanceAssert inHistory() {
+ return assertThat(processServicesProvider.getHistoryService().createHistoricProcessInstanceQuery().processInstanceId(actual.getId()).singleResult());
+ }
+
+ /**
+ * Assert list of runtime process instance activities ordered by activity start time.
+ *
+ * @return Assertion of #{@link ActivityInstance} list.
+ */
+ public ListAssert activities() {
+ isNotNull();
+
+ return assertThat(processServicesProvider.getRuntimeService().createActivityInstanceQuery().processInstanceId(actual.getId()).orderByActivityInstanceStartTime().asc().list());
+ }
+
+ /**
+ * Assert list of runtime execution instances without ordering.
+ *
+ * @return Assertion of #{@link Execution} list.
+ */
+ public ListAssert executions() {
+ isNotNull();
+
+ return assertThat(processServicesProvider.getRuntimeService().createExecutionQuery().processInstanceId(actual.getId()).list());
+ }
+
+ /**
+ * Assert list of runtime variable instances ordered by variable name ascending.
+ *
+ * @return Assertion of #{@link VariableInstance} list.
+ */
+ public ListAssert variables() {
+ isNotNull();
+
+ return assertThat(processServicesProvider.getRuntimeService().createVariableInstanceQuery().processInstanceId(actual.getId()).orderByVariableName().asc().list());
+ }
+
+ /**
+ * Assert list of runtime identity links without ordering.
+ *
+ * @return Assertion of #{@link IdentityLink} list.
+ */
+ public ListAssert identityLinks() {
+ isNotNull();
+
+ return assertThat(processServicesProvider.getRuntimeService().getIdentityLinksForProcessInstance(actual.getId()));
+ }
+
+ /**
+ * Assert list of user tasks in the runtime ordered by the task name ascending.
+ *
+ * @return Assertion of {@link Task} list.
+ */
+ public ListAssert userTasks() {
+ isNotNull();
+
+ return assertThat(Utils.getTaskService().createTaskQuery().processInstanceId(actual.getId()).orderByTaskName().asc()
+ .includeProcessVariables().includeIdentityLinks().includeTaskLocalVariables().list());
+ }
+
+ /**
+ * Assert list of event subscriptions in the runtime ordered by the event name ascending.
+ *
+ * @return Assertion of {@link EventSubscription} list.
+ */
+
+ public ListAssert eventSubscription() {
+ isNotNull();
+
+ return assertThat(processServicesProvider.getRuntimeService().createEventSubscriptionQuery().processInstanceId(actual.getId()).orderByEventName().asc().list());
+ }
+}
diff --git a/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessServicesProvider.java b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessServicesProvider.java
new file mode 100644
index 00000000000..5a13ed5ef87
--- /dev/null
+++ b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/ProcessServicesProvider.java
@@ -0,0 +1,41 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.ProcessEngine;
+import org.flowable.engine.RuntimeService;
+
+/**
+ * @author martin.grofcik
+ */
+public class ProcessServicesProvider {
+ final ProcessEngine processEngine;
+
+ private ProcessServicesProvider(ProcessEngine processEngine) {
+ this.processEngine = processEngine;
+ }
+
+ static ProcessServicesProvider of(ProcessEngine processEngine) {
+ return new ProcessServicesProvider(processEngine);
+ }
+
+ RuntimeService getRuntimeService() {
+ return processEngine.getRuntimeService();
+ }
+
+ HistoryService getHistoryService() {
+ return processEngine.getHistoryService();
+ }
+}
diff --git a/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/Utils.java b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/Utils.java
new file mode 100644
index 00000000000..12da07c1e10
--- /dev/null
+++ b/modules/flowable-assertj/src/main/java/org/flowable/assertj/process/Utils.java
@@ -0,0 +1,45 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.flowable.engine.*;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+
+/**
+ * @author martin.grofcik
+ */
+public class Utils {
+
+ protected static String getProcessDescription(ProcessInstance actual) {
+ return getProcessDescription(actual.getProcessDefinitionKey(), actual.getId());
+ }
+
+ protected static String getProcessDescription(HistoricProcessInstance actual) {
+ return getProcessDescription(actual.getProcessDefinitionKey(), actual.getId());
+ }
+
+ protected static String getProcessDescription(String processDefinitionKey, String id) {
+ return "Expected process instance <"+processDefinitionKey+", "+id+">";
+ }
+
+ protected static TaskService getTaskService() {
+ return getProcessEngine().getTaskService();
+ }
+
+ protected static ProcessEngine getProcessEngine() {
+ return ProcessEngines.getProcessEngines().get("default");
+ }
+
+}
diff --git a/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/HistoricProcessInstanceAssertTest.java b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/HistoricProcessInstanceAssertTest.java
new file mode 100644
index 00000000000..4db6d14735a
--- /dev/null
+++ b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/HistoricProcessInstanceAssertTest.java
@@ -0,0 +1,127 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.assertj.core.groups.Tuple;
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.history.HistoricProcessInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.engine.test.FlowableTest;
+import org.flowable.task.api.Task;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * @author martin.grofcik
+ */
+@FlowableTest
+class HistoricProcessInstanceAssertTest {
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void isFinishedForFinishedProcessInstance(RuntimeService runtimeService, TaskService taskService, HistoryService historyService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.inHistory().activities().extracting(HistoricActivityInstance::getActivityId).contains(
+ "theStart", "theStart-theTask", "theTask"
+ );
+
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ assertThatOneTaskProcess.inHistory().isFinished()
+ .activities().extracting(HistoricActivityInstance::getActivityId).contains(
+ "theStart", "theStart-theTask", "theTask", "theTask-theEnd", "theEnd"
+ );
+
+ HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(oneTaskProcess.getId()).singleResult();
+ FlowableProcessAssertions.assertThat(historicProcessInstance).isFinished()
+ .activities().extracting(HistoricActivityInstance::getActivityId).contains(
+ "theStart", "theStart-theTask", "theTask", "theTask-theEnd", "theEnd"
+ );
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void variables(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.as("No variable exists in the process scope.")
+ .inHistory().variables().isEmpty();
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "testVariable", "variableValue");
+
+ assertThatOneTaskProcess.as("Variable exists in the process scope, the variable must be present in the history.")
+ .inHistory()
+ .hasVariable("testVariable")
+ .hasVariableWithValue("testVariable", "variableValue")
+ .variables().hasSize(1).extracting("name", "value").
+ containsExactly(Tuple.tuple("testVariable", "variableValue"));
+
+ Task task = taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult();
+ taskService.complete(task.getId());
+
+ assertThatOneTaskProcess.as("Variable exists in the process scope, the variable must be present in the history.")
+ .doesNotExist()
+ .inHistory()
+ .isFinished()
+ .hasVariable("testVariable")
+ .hasVariableWithValue("testVariable", "variableValue")
+ .variables().hasSize(1).extracting("name", "value").
+ containsExactly(Tuple.tuple("testVariable", "variableValue"));
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void hasVariable(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.as("No variable exists in the process scope.")
+ .inHistory().variables().isEmpty();
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "testVariable", "variableValue");
+
+ assertThatOneTaskProcess.as("Variable exists in the process scope, the variable must be present in the history.")
+ .inHistory().variables().hasSize(1).extracting("name", "value").
+ containsExactly(Tuple.tuple("testVariable", "variableValue"));
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void doesNotHaveVariable(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.as("No variable exists in the process scope.")
+ .inHistory().doesNotHaveVariable("nonExistingVariable");
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "testVariable", "variableValue");
+
+ assertThatOneTaskProcess.as("Variable exists in the process scope, the variable must be present in the history.")
+ .inHistory().doesNotHaveVariable("nonExistingVariable")
+ .hasVariable("testVariable");
+
+ assertThatThrownBy(() -> assertThatOneTaskProcess.inHistory().doesNotHaveVariable("testVariable"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance does not have variable but variable exists in history.");
+ }
+
+}
\ No newline at end of file
diff --git a/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/LongRunningProcessInstanceAssertTest.java b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/LongRunningProcessInstanceAssertTest.java
new file mode 100644
index 00000000000..243b4793fec
--- /dev/null
+++ b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/LongRunningProcessInstanceAssertTest.java
@@ -0,0 +1,62 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.runtime.ActivityInstance;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.engine.test.FlowableTest;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author martin.grofcik
+ */
+@FlowableTest
+class LongRunningProcessInstanceAssertTest {
+
+ @Test
+ @Deployment(resources = "twoTasks.bpmn20.xml")
+ void useOneAssertInstanceThroughAllTest(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance twoTasksProcess = runtimeService.createProcessInstanceBuilder().processDefinitionKey("twoTasksProcess").start();
+ ProcessInstanceAssert asserThatProcessInstance = FlowableProcessAssertions.assertThat(
+ twoTasksProcess
+ );
+ asserThatProcessInstance.isRunning()
+ .activities().extracting(ActivityInstance::getActivityId).containsExactlyInAnyOrder(
+ "theStart", "theStart-theTask", "theTask1"
+ );
+
+ taskService.complete(geTaskIdForProcessInstance(twoTasksProcess.getId(), taskService));
+
+ asserThatProcessInstance.isRunning().activities().extracting(ActivityInstance::getActivityId).containsExactlyInAnyOrder(
+ "theStart", "theStart-theTask", "theTask1", "theTask1-theTask2", "theTask2"
+ );
+
+ taskService.complete(geTaskIdForProcessInstance(twoTasksProcess.getId(), taskService));
+
+ asserThatProcessInstance.doesNotExist().inHistory()
+ .activities().extracting(HistoricActivityInstance::getActivityId).containsExactlyInAnyOrder(
+ "theStart", "theStart-theTask", "theTask1", "theTask1-theTask2", "theTask2", "theTask-theEnd",
+ "theEnd"
+ );
+ }
+
+ private String geTaskIdForProcessInstance(String processInstanceId, TaskService taskService) {
+ return taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult().getId();
+ }
+
+}
\ No newline at end of file
diff --git a/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/ProcessInstanceAssertTest.java b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/ProcessInstanceAssertTest.java
new file mode 100644
index 00000000000..c4d09e6aa09
--- /dev/null
+++ b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/ProcessInstanceAssertTest.java
@@ -0,0 +1,308 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.assertj.core.groups.Tuple;
+import org.flowable.engine.ManagementService;
+import org.flowable.engine.ProcessEngineConfiguration;
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.TaskService;
+import org.flowable.engine.impl.test.JobTestHelper;
+import org.flowable.engine.runtime.ActivityInstance;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.test.Deployment;
+import org.flowable.engine.test.FlowableTest;
+import org.flowable.eventsubscription.api.EventSubscription;
+import org.flowable.identitylink.api.IdentityLink;
+import org.flowable.identitylink.api.IdentityLinkType;
+import org.flowable.identitylink.api.history.HistoricIdentityLink;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * @author martin.grofcik
+ */
+@FlowableTest
+class ProcessInstanceAssertTest {
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void isRunning(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ FlowableProcessAssertions.assertThat(oneTaskProcess).isRunning();
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void isRunningForNonRunningProcess(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat(oneTaskProcess).isRunning())
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance to be running but is not.");
+ }
+
+ @Test
+ void isRunningForNull() {
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat((ProcessInstance) null).isRunning())
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expecting actual not to be null");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void hasVariable(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ runtimeService.setVariable(oneTaskProcess.getId(), "variableName", "variableValue");
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.hasVariable("variableName");
+ assertThatOneTaskProcess.hasVariable("variableName").isRunning();
+ assertThatOneTaskProcess.isRunning().hasVariable("variableName");
+ assertThatThrownBy(() -> assertThatOneTaskProcess.hasVariable("nonExistingVariable"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance has variable but variable does not exist.");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void hasVariableForNonRunningProcess(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ runtimeService.setVariable(oneTaskProcess.getId(), "variableName", "variableValue");
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat(oneTaskProcess).hasVariable("variableName"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance has variable but variable does not exist.");
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat(oneTaskProcess).hasVariable("nonExistingVariable"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance has variable but variable does not exist.");
+ }
+
+ @Test
+ void hasVariableForNull() {
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat((ProcessInstance) null).hasVariable("variableName"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expecting actual not to be null");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void doesNotHaveVariable(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ runtimeService.setVariable(oneTaskProcess.getId(), "variableName", "variableValue");
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.doesNotHaveVariable("NonExistingVariableName");
+ assertThatOneTaskProcess.doesNotHaveVariable("NonExistingVariableName").isRunning();
+ assertThatThrownBy(() -> assertThatOneTaskProcess.doesNotHaveVariable("variableName"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance does not have variable but variable exists.");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void doesNotHaveVariableForNonRunningProcess(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ runtimeService.setVariable(oneTaskProcess.getId(), "variableName", "variableValue");
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.doesNotHaveVariable("variableName");
+ assertThatOneTaskProcess.doesNotHaveVariable("nonExistingVariable");
+ }
+
+ @Test
+ void doesNotHaveVariableForNull() {
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat((ProcessInstance) null).doesNotHaveVariable("variableName"))
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expecting actual not to be null");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void doesNotExistForRunningInstance(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat(oneTaskProcess).doesNotExist())
+ .isInstanceOf(AssertionError.class)
+ .hasMessage("Expected process instance is finished but instance exists in runtime.");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void doesNotExistForNonRunningProcess(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ FlowableProcessAssertions.assertThat(oneTaskProcess).doesNotExist();
+ }
+
+ @Test
+ void doesNotExistForNull() {
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat((ProcessInstance) null).doesNotExist())
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expecting actual not to be null");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void activitiesForRunningInstance(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ FlowableProcessAssertions.assertThat(oneTaskProcess).activities().extracting(ActivityInstance::getActivityId)
+ .contains("theStart", "theStart-theTask", "theTask");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void activitiesForNonRunningProcess(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ FlowableProcessAssertions.assertThat(oneTaskProcess).activities().isEmpty();
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void executions(RuntimeService runtimeService, TaskService taskService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.as("Running process has at least 2 active executions." +
+ "(ProcessInstance + Child)")
+ .executions().extracting(Execution::getId).contains(oneTaskProcess.getId())
+ .hasSize(2);
+
+ taskService.complete(taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult().getId());
+
+ assertThatOneTaskProcess.doesNotExist().as("There must be no execution for the finished process.")
+ .executions().hasSize(0);
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void variables(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.variables().isEmpty();
+ assertThatThrownBy(() -> assertThatOneTaskProcess.hasVariableWithValue("nonExistingVar", "anyValue"))
+ .isInstanceOf(AssertionError.class).hasMessage("Expected process instance has variable but variable does not exist.");
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "testVariable", "initialValue");
+
+ assertThatOneTaskProcess.variables().extracting("name", "value").contains(Tuple.tuple("testVariable", "initialValue"));
+ assertThatOneTaskProcess.hasVariableWithValue("testVariable", "initialValue");
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "testVariable", "updatedValue");
+
+ assertThatOneTaskProcess.variables().extracting("name", "value").contains(Tuple.tuple("testVariable", "updatedValue"));
+ assertThatOneTaskProcess.hasVariableWithValue("testVariable", "updatedValue").isRunning();
+
+ runtimeService.setVariable(oneTaskProcess.getId(), "firstVariable", "initialValue");
+ assertThatOneTaskProcess.as("Variables are ordered by names ascending.")
+ .variables().extracting("name")
+ .containsExactly("firstVariable", "testVariable");
+
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void identityLinks(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = TestUtils.createOneTaskProcess(runtimeService);
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.identityLinks().isEmpty();
+ assertThatOneTaskProcess.inHistory().identityLinks().isEmpty();
+
+ runtimeService.addUserIdentityLink(oneTaskProcess.getId(), "testUser", IdentityLinkType.ASSIGNEE);
+ runtimeService.addGroupIdentityLink(oneTaskProcess.getId(), "testGroup", IdentityLinkType.CANDIDATE);
+
+ assertThatOneTaskProcess.identityLinks().hasSize(2)
+ .extracting(IdentityLink::getUserId, IdentityLink::getGroupId, IdentityLink::getType)
+ .containsExactlyInAnyOrder(Tuple.tuple("testUser", null, IdentityLinkType.ASSIGNEE),
+ Tuple.tuple(null, "testGroup", IdentityLinkType.CANDIDATE));
+ assertThatOneTaskProcess.inHistory().identityLinks().hasSize(2)
+ .extracting(HistoricIdentityLink::getUserId, HistoricIdentityLink::getGroupId, HistoricIdentityLink::getType)
+ .containsExactlyInAnyOrder(Tuple.tuple("testUser", null, IdentityLinkType.ASSIGNEE),
+ Tuple.tuple(null, "testGroup", IdentityLinkType.CANDIDATE));
+
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void userTasks(RuntimeService runtimeService, ManagementService managementService,
+ TaskService taskService, ProcessEngineConfiguration processEngineConfiguration) {
+ ProcessInstance oneTaskProcess = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess").startAsync();
+
+ ProcessInstanceAssert assertThatOneTaskProcess = FlowableProcessAssertions.assertThat(oneTaskProcess);
+ assertThatOneTaskProcess.as("Process instance is started asynchronously and did not reach userTask")
+ .userTasks().isEmpty();
+ assertThatOneTaskProcess.as("Process instance is started asynchronously and did not reach userTask in history")
+ .inHistory().userTasks().isEmpty();
+
+ JobTestHelper.waitForJobExecutorToProcessAllJobs(processEngineConfiguration, managementService, 10_000, 500);
+
+ assertThatOneTaskProcess.as("Async executions executed and userTask reached.")
+ .userTasks().hasSize(1).extracting(Task::getTaskDefinitionKey).containsExactly("theTask");
+ assertThatOneTaskProcess.as("Async executions executed and userTask reached in history.")
+ .inHistory().userTasks().hasSize(1).extracting(HistoricTaskInstance::getTaskDefinitionKey)
+ .containsExactly("theTask");
+
+ Task task = taskService.createTaskQuery().processInstanceId(oneTaskProcess.getId()).singleResult();
+ taskService.complete(task.getId());
+
+ assertThatOneTaskProcess.as("User tasks are empty for non existing process").doesNotExist()
+ .userTasks().isEmpty();
+ assertThatOneTaskProcess.as("The userTask is completed, but must exist in history.")
+ .inHistory().isFinished()
+ .userTasks().hasSize(1).extracting(HistoricTaskInstance::getTaskDefinitionKey)
+ .containsExactly("theTask");
+ }
+
+ @Test
+ @Deployment(resources = "oneTask.bpmn20.xml")
+ void subscriptionsWithoutSubscription(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcess = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess").start();
+
+ FlowableProcessAssertions.assertThat(oneTaskProcess).as("One task process does not have any subscription")
+ .eventSubscription().isEmpty();
+
+ }
+
+ @Test
+ @Deployment(resources = "oneTaskWithBoundaryEvent.bpmn20.xml")
+ void subscriptions(RuntimeService runtimeService) {
+ ProcessInstance oneTaskProcessWithSubscription = runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcessWithBoundaryEvent").start();
+
+ FlowableProcessAssertions.assertThat(oneTaskProcessWithSubscription).as("One task process with subscription has exactly one subscription")
+ .eventSubscription().hasSize(1).extracting(EventSubscription::getEventName).contains("eventMessage");
+ }
+
+ @Test
+ void activitiesForNull() {
+ assertThatThrownBy(() -> FlowableProcessAssertions.assertThat((ProcessInstance) null).doesNotExist())
+ .isInstanceOf(AssertionError.class)
+ .hasMessageContaining("Expecting actual not to be null");
+ }
+
+}
\ No newline at end of file
diff --git a/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/TestUtils.java b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/TestUtils.java
new file mode 100644
index 00000000000..de2fd1e69f3
--- /dev/null
+++ b/modules/flowable-assertj/src/test/java/org/flowable/assertj/process/TestUtils.java
@@ -0,0 +1,26 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * 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.flowable.assertj.process;
+
+import org.flowable.engine.RuntimeService;
+import org.flowable.engine.runtime.ProcessInstance;
+
+/**
+ * @author martin.grofcik
+ */
+abstract class TestUtils {
+ static ProcessInstance createOneTaskProcess(RuntimeService runtimeService) {
+ return runtimeService.createProcessInstanceBuilder().processDefinitionKey("oneTaskProcess").start();
+ }
+}
diff --git a/modules/flowable-assertj/src/test/resources/flowable.cfg.xml b/modules/flowable-assertj/src/test/resources/flowable.cfg.xml
new file mode 100644
index 00000000000..9f1f9937cc2
--- /dev/null
+++ b/modules/flowable-assertj/src/test/resources/flowable.cfg.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/flowable-assertj/src/test/resources/log4j.properties b/modules/flowable-assertj/src/test/resources/log4j.properties
new file mode 100644
index 00000000000..3cb5809086f
--- /dev/null
+++ b/modules/flowable-assertj/src/test/resources/log4j.properties
@@ -0,0 +1,9 @@
+log4j.rootLogger=INFO, CA
+
+# ConsoleAppender
+log4j.appender.CA=org.apache.log4j.ConsoleAppender
+log4j.appender.CA.layout=org.apache.log4j.PatternLayout
+log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
+
+log4j.logger.org.apache.ibatis=INFO
+log4j.logger.javax.activation=INFO
diff --git a/modules/flowable-assertj/src/test/resources/oneTask.bpmn20.xml b/modules/flowable-assertj/src/test/resources/oneTask.bpmn20.xml
new file mode 100644
index 00000000000..1649c6f4ff4
--- /dev/null
+++ b/modules/flowable-assertj/src/test/resources/oneTask.bpmn20.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/flowable-assertj/src/test/resources/oneTaskWithBoundaryEvent.bpmn20.xml b/modules/flowable-assertj/src/test/resources/oneTaskWithBoundaryEvent.bpmn20.xml
new file mode 100644
index 00000000000..53bad04ca24
--- /dev/null
+++ b/modules/flowable-assertj/src/test/resources/oneTaskWithBoundaryEvent.bpmn20.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/flowable-assertj/src/test/resources/twoTasks.bpmn20.xml b/modules/flowable-assertj/src/test/resources/twoTasks.bpmn20.xml
new file mode 100644
index 00000000000..accf69c9c30
--- /dev/null
+++ b/modules/flowable-assertj/src/test/resources/twoTasks.bpmn20.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 3a76fe2bcb9..3ca4e154691 100644
--- a/pom.xml
+++ b/pom.xml
@@ -536,6 +536,7 @@
modules/flowable-spring-security
modules/flowable-http-common
modules/flowable-mail
+ modules/flowable-assertj
@@ -1023,6 +1024,7 @@
modules/flowable-ldap
modules/flowable-ldap-configurator
modules/flowable-jmx
+ modules/flowable-assertj
tooling/archetypes/flowable-archetype-unittest