diff --git a/.github/release-notes.yml b/.github/release-notes.yml
new file mode 100644
index 0000000..1686110
--- /dev/null
+++ b/.github/release-notes.yml
@@ -0,0 +1,14 @@
+changelog:
+ sections:
+ - title: ":rocket: Enhancements & Features"
+ labels: [ "Type: enhancement", "Type: documentation", "Type: example" ]
+ - title: ":bug: Bug Fixes"
+ labels: [ "Type: bug" ]
+ - title: ":hammer_and_wrench: Chore"
+ labels: [ "Type: dependencies" ]
+ issues:
+ exclude:
+ labels: [ "Type: Incorrect Repository", "Type: question" ]
+ contributors:
+ exclude:
+ names: [ "dependabot[bot]", "codacy-badger" ]
diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml
index b1ba557..9533dd1 100644
--- a/.github/workflows/default.yml
+++ b/.github/workflows/default.yml
@@ -38,7 +38,3 @@ jobs:
- name: Build with Maven
run: ./mvnw clean verify -U -B -T4
- - name: Upolad coverage information
- uses: codecov/codecov-action@v1
- with:
- token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 7da8d23..9b710ec 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -46,8 +46,3 @@ jobs:
env:
OSS_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
OSS_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
-
- - name: Upolad coverage information
- uses: codecov/codecov-action@v1
- with:
- token: ${{ secrets.CODECOV_TOKEN }}
diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml
new file mode 100644
index 0000000..5e7360d
--- /dev/null
+++ b/.github/workflows/release-notes.yml
@@ -0,0 +1,33 @@
+# Trigger the workflow on milestone events
+on:
+ milestone:
+ types: [closed]
+name: Milestone Closure
+jobs:
+ create-release-notes:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@master
+ - name: Create Release Notes Markdown
+ uses: docker://decathlon/release-notes-generator-action:3.1.5
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
+ OUTPUT_FOLDER: temp_release_notes
+ USE_MILESTONE_TITLE: "true"
+ - name: Get the name of the created Release Notes file and extract Version
+ run: |
+ RELEASE_NOTES_FILE=$(ls temp_release_notes/*.md | head -n 1)
+ echo "RELEASE_NOTES_FILE=$RELEASE_NOTES_FILE" >> $GITHUB_ENV
+ VERSION=$(echo ${{ github.event.milestone.title }} | cut -d' ' -f2)
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+ - name: Create a Draft Release Notes on GitHub
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
+ with:
+ tag_name: ${{ env.VERSION }}
+ release_name: ${{ env.VERSION }}
+ body_path: ${{ env.RELEASE_NOTES_FILE }}
+ draft: true
diff --git a/README.md b/README.md
index d5c7f91..bf10486 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,6 @@ Camunda specific stages and scenarios for the BDD testing tool JGiven written in
[![Development braches](https://github.com/holunda-io/camunda-bpm-jgiven/workflows/Development%20braches/badge.svg)](https://github.com/holunda-io/camunda-bpm-jgiven/workflows)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.holunda.testing/camunda-bpm-jgiven/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.holunda.testing/camunda-bpm-jgiven)
-[![codecov](https://codecov.io/gh/holunda-io/camunda-bpm-jgiven/branch/master/graph/badge.svg)](https://codecov.io/gh/holunda-io/camunda-bpm-jgiven)
[![Project Stats](https://www.openhub.net/p/camunda-bpm-jgiven/widgets/project_thin_badge.gif)](https://www.openhub.net/p/camunda-bpm-jgiven)
@@ -14,24 +13,25 @@ Starting from 2012, we are preaching that processes are no units. Behavior-drive
underlying testing methodology of scenario-based testing is a way more adequate and convenient for writing
process (model) tests.
-Our first attempts addressed testing frameworks Cucumber and JBehave. For JBehave we were even able to release
-an official [Camunda BPM extension](https://github.com/camunda/camunda-bpm-jbehave). It turned out that the main problem
-in using it, was error-prone writing of the test specifications in Gherkin and glue code in Java.
+Our first attempts addressed testing frameworks Cucumber and JBehave. For JBehave we were able to release
+an official [Camunda BPM extension](https://github.com/camunda/camunda-bpm-jbehave), but it turned out that the main problem
+in using it, was error-prone writing of the test specifications in Gherkin (text files) and glue code them with Java.
This is, where [JGiven](http://jgiven.org/) comes on the scene, allowing to write both in Java or any other JVM language
-by providing a nice API and later generating reports which are human readable.
+by providing a nice API and later generating reports which are human-readable.
## Usage
Add the following dependency to your Maven pom:
-
- io.holunda.testing
- camunda-bpm-jgiven
- 0.0.7
- test
-
-
+```xml
+
+ io.holunda.testing
+ camunda-bpm-jgiven
+ 0.2.0
+ test
+
+```
## Features
JGiven supports separation of the glue code (application driver) into so-called [stages](http://jgiven.org/userguide/#_stages_and_state_sharing).
@@ -39,60 +39,113 @@ Stages contain assert and action methods and may be subclassed. This library pro
`ProcessStage` for building your process testing stages. Here is how the test then looks like
(written in Kotlin):
+### JUnit4
+
+```kotlin
+@Deployment(resources = [ApprovalProcessBean.RESOURCE])
+open class ApprovalProcessTest : ScenarioTest() {
+
+ @get: Rule
+ val rule: ProcessEngineRule = StandaloneInMemoryTestConfiguration().rule()
+
+ @ScenarioState
+ val camunda = rule.processEngine
+
@Test
- fun `should automatically approve`() {
-
- val approvalRequestId = UUID.randomUUID().toString()
-
- given()
- .process_is_deployed(ApprovalProcessBean.KEY)
- .and()
- .process_is_started_for_request(approvalRequestId)
- .and()
- .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
- .and()
- .automatic_approval_returns(Expressions.ApprovalDecision.APPROVE)
-
- whenever()
- .process_continues()
-
- then()
- .process_is_finished()
- .and()
- .process_has_passed(Elements.SERVICE_AUTO_APPROVE, Elements.END_APPROVED)
-
+ internal fun `should automatically approve`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
+ .AND
+ .automatic_approval_returns(Expressions.ApprovalDecision.APPROVE)
+
+ WHEN
+ .process_continues()
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.SERVICE_AUTO_APPROVE, Elements.END_APPROVED)
+
}
+}
+```
+
+### JUnit5
+
+```kotlin
+@ExtendWith(ProcessEngineExtension::class)
+@Deployment(resources = [ApprovalProcessBean.RESOURCE])
+internal class ApprovalProcessTest :
+ ScenarioTest() {
+
+ @RegisterExtension
+ val extension = TestProcessEngine.DEFAULT
+
+ @ScenarioState
+ val camunda = extension.processEngine
+
+ @Test
+ internal fun `should automatically approve`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
+ .AND
+ .automatic_approval_returns(Expressions.ApprovalDecision.APPROVE)
+
+ WHEN
+ .process_continues()
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.SERVICE_AUTO_APPROVE, Elements.END_APPROVED)
-And here is the corresponding stage:
-
- open class ApprovalProcessActionStage : ProcessStage() {
-
- @BeforeStage
- open fun `automock all delegates`() {
- CamundaMockito.registerJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY)
- CamundaMockito.registerJavaDelegateMock(AUTOMATICALLY_APPROVE_REQUEST)
- CamundaMockito.registerJavaDelegateMock(ApprovalProcessBean.Expressions.LOAD_APPROVAL_REQUEST)
- }
-
- open fun process_is_started_for_request(approvalRequestId: String): ApprovalProcessActionStage {
- processInstanceSupplier = ApprovalProcessBean(camunda.processEngine)
- processInstanceSupplier.start(approvalRequestId)
- assertThat(processInstanceSupplier.processInstance).isNotNull
- assertThat(processInstanceSupplier.processInstance).isStarted
- return self()
- }
-
- fun approval_strategy_can_be_applied(approvalStrategy: String): ApprovalProcessActionStage {
- getJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY).onExecutionSetVariables(Variables.putValue(APPROVAL_STRATEGY, approvalStrategy))
- return self()
- }
-
- fun automatic_approval_returns(approvalDecision: String): ApprovalProcessActionStage {
- getJavaDelegateMock(AUTOMATICALLY_APPROVE_REQUEST).onExecutionSetVariables(Variables.putValue(APPROVAL_DECISION, approvalDecision))
- return self()
- }
}
-
+}
+```
+
+Here is the corresponding stage, providing the steps used in the test:
+
+```kotlin
+class ApprovalProcessActionStage : ProcessStage() {
+
+ @BeforeStage
+ fun `automock all delegates`() {
+ CamundaMockito.registerJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY)
+ CamundaMockito.registerJavaDelegateMock(AUTOMATICALLY_APPROVE_REQUEST)
+ CamundaMockito.registerJavaDelegateMock(ApprovalProcessBean.Expressions.LOAD_APPROVAL_REQUEST)
+ }
+
+ fun process_is_started_for_request(approvalRequestId: String) = step {
+ processInstanceSupplier = ApprovalProcessBean(camunda.processEngine)
+ processInstanceSupplier.start(approvalRequestId)
+ assertThat(processInstanceSupplier.processInstance).isNotNull
+ assertThat(processInstanceSupplier.processInstance).isStarted
+ }
+
+ fun approval_strategy_can_be_applied(approvalStrategy: String) = step {
+ getJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY).onExecutionSetVariables(Variables.putValue(APPROVAL_STRATEGY, approvalStrategy))
+ }
+
+ fun automatic_approval_returns(approvalDecision: String) = step {
+ getJavaDelegateMock(AUTOMATICALLY_APPROVE_REQUEST).onExecutionSetVariables(Variables.putValue(APPROVAL_DECISION, approvalDecision))
+ }
+}
+```
+
The resulting report:
![JGiven Process Report](docs/report.png)
@@ -115,12 +168,8 @@ If you have permissions to release, make sure all branches are fetched and run:
./mvnw gitflow:release-start
./mvnw gitflow:release-finish
-from cli. This will update the poms of `develop` and `master` branches.
-If you want to publish to central and have sufficient permissions, run
-
- ./mvnw clean deploy -Prelease -DskipExamples
-
-on `master` branch. Don't forget to close and release repository on https://oss.sonatype.org/#stagingRepositories.
+from cli. This will update the poms of `develop` and `master` branches
+and start GitHub actions producing a new release.
### Current maintainers
@@ -130,5 +179,3 @@ on `master` branch. Don't forget to close and release repository on https://oss.
* [Jan Galinski](https://github.com/jangalinski)
* [Andre Hegerath](https://github.com/a-hegerath)
* [Stefan Zilske](https://github.com/stefanzilske)
-
-
diff --git a/examples/basic-junit5/pom.xml b/examples/basic-junit5/pom.xml
new file mode 100644
index 0000000..6a4bf44
--- /dev/null
+++ b/examples/basic-junit5/pom.xml
@@ -0,0 +1,81 @@
+
+
+ 4.0.0
+
+
+ io.holunda.testing
+ camunda-bpm-jgiven-examples
+ 0.2.0
+
+
+ camunda-bpm-jgiven-examples-basic-junit5
+ jar
+
+
+
+ org.camunda.bpm.springboot
+ camunda-bpm-spring-boot-starter-webapp
+
+
+ com.h2database
+ h2
+
+
+ org.camunda.bpm.extension.mockito
+ camunda-bpm-mockito
+ 5.16.0
+ test
+
+
+ org.camunda.bpm.springboot
+ camunda-bpm-spring-boot-starter-test
+ test
+
+
+ org.camunda.bpm.assert
+ camunda-bpm-assert-assertj3-11-1
+
+
+
+
+ org.camunda.bpm.assert
+ camunda-bpm-assert
+ test
+
+
+ org.camunda.bpm.extension
+ camunda-bpm-junit5
+ 1.1.0
+ test
+
+
+ io.holunda.testing
+ camunda-bpm-jgiven
+ test
+
+
+ org.camunda.bpm.extension
+ camunda-bpm-process-test-coverage
+ 0.4.0
+ test
+
+
+
+
+
+
+
+ kotlin-maven-plugin
+ org.jetbrains.kotlin
+
+
+
+ com.tngtech.jgiven
+ jgiven-maven-plugin
+
+
+
+
+
+
diff --git a/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/ApprovalProcessBean.kt b/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/ApprovalProcessBean.kt
new file mode 100644
index 0000000..6b3ef0f
--- /dev/null
+++ b/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/ApprovalProcessBean.kt
@@ -0,0 +1,69 @@
+package io.holunda.testing.examples.basic
+
+import org.camunda.bpm.engine.ProcessEngine
+import org.camunda.bpm.engine.runtime.ProcessInstance
+import java.util.function.Supplier
+
+class ApprovalProcessBean(private val processEngine: ProcessEngine) : Supplier {
+
+ companion object {
+ const val KEY = "approval"
+ const val RESOURCE = "approval.bpmn"
+ }
+
+ lateinit var processInstance: ProcessInstance
+
+ override fun get(): ProcessInstance = this.processInstance
+
+ object Elements {
+ const val START = "start"
+ const val END_CANCELLED = "end_cancelled"
+ const val END_APPROVED = "end_approved"
+ const val END_REJECTED = "end_rejected"
+ const val USER_APPROVE_REQUEST = "user_approve_request"
+ const val USER_AMEND_REQUEST = "user_amend_request"
+ const val SERVICE_AUTO_APPROVE = "service_auto_approve_request"
+ }
+
+ object Variables {
+ const val APPROVAL_REQUEST_ID = "approvalRequestId"
+ const val APPROVAL_STRATEGY = "approvalStrategy"
+ const val APPROVAL_DECISION = "approvalDecision"
+ const val AMEND_ACTION = "ammendAction"
+ const val ORIGINATOR = "originator"
+ }
+
+ object Expressions {
+ const val LOAD_APPROVAL_REQUEST = "loadApprovalRequest"
+ const val DETERMINE_APPROVAL_STRATEGY = "determineApprovalStrategy"
+ const val AUTOMATICALLY_APPROVE_REQUEST = "automaticallyApproveRequest"
+ const val AUTOMATIC_APPROVAL_FAILED = "automaticApprovalFailed"
+ const val APPROVE_REQUEST_TASK_LISTENER = "approveRequestTaskListener"
+
+ object ApprovalStrategy {
+ const val AUTOMATIC = "AUTOMATIC"
+ const val MANUAL = "MANUAL"
+ }
+
+ object ApprovalDecision {
+ const val APPROVE = "APPROVE"
+ const val REJECT = "REJECT"
+ const val RETURN = "RETURN"
+ }
+
+ object AmendAction {
+ const val RESUBMIT = "RESUBMIT"
+ const val CANCEL = "CANCEL"
+ }
+ }
+
+ fun start(approvalRequestId: String) {
+ this.processInstance = this.processEngine.runtimeService.startProcessInstanceByKey(
+ KEY,
+ approvalRequestId,
+ org.camunda.bpm.engine.variable.Variables
+ .putValue(Variables.ORIGINATOR, "kermit")
+ .putValue(Variables.APPROVAL_REQUEST_ID, approvalRequestId)
+ )
+ }
+}
diff --git a/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/BasicProcessApplication.kt b/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/BasicProcessApplication.kt
new file mode 100644
index 0000000..99f8a84
--- /dev/null
+++ b/examples/basic-junit5/src/main/kotlin/io/holunda/testing/examples/basic/BasicProcessApplication.kt
@@ -0,0 +1,20 @@
+package io.holunda.testing.examples.basic
+
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Expressions.APPROVE_REQUEST_TASK_LISTENER
+import org.camunda.bpm.engine.delegate.TaskListener
+import org.camunda.bpm.spring.boot.starter.annotation.EnableProcessApplication
+import org.springframework.boot.runApplication
+import org.springframework.context.annotation.Bean
+import java.time.temporal.ChronoUnit
+import java.util.*
+
+@EnableProcessApplication
+class BasicProcessApplication {
+ fun main(args: Array) = runApplication(*args).let { Unit }
+
+ @Bean(APPROVE_REQUEST_TASK_LISTENER)
+ fun approveRequestTaskListener() = TaskListener {
+ it.followUpDate = Date.from(it.createTime.toInstant().plus(1, ChronoUnit.DAYS))
+ }
+
+}
diff --git a/examples/basic-junit5/src/main/resources/META-INF/processes.xml b/examples/basic-junit5/src/main/resources/META-INF/processes.xml
new file mode 100644
index 0000000..e69de29
diff --git a/examples/basic-junit5/src/main/resources/approval.bpmn b/examples/basic-junit5/src/main/resources/approval.bpmn
new file mode 100644
index 0000000..506d402
--- /dev/null
+++ b/examples/basic-junit5/src/main/resources/approval.bpmn
@@ -0,0 +1,313 @@
+
+
+
+
+ flow1
+
+
+
+ SequenceFlow_1p4jixi
+ SequenceFlow_1aallpn
+
+
+
+ SequenceFlow_1eaf9ol
+ SequenceFlow_064ef2y
+ SequenceFlow_0n52bjk
+
+
+
+ ${approvalStrategy == "AUTOMATIC"}
+
+
+ SequenceFlow_1wtga4h
+ SequenceFlow_1ccqf4c
+ SequenceFlow_0gcf7ty
+
+
+
+ SequenceFlow_0gcf7ty
+ SequenceFlow_0jqf4xj
+ SequenceFlow_1i81jsh
+ SequenceFlow_0kbwukz
+
+
+
+ SequenceFlow_0jqf4xj
+
+
+ ${approvalDecision == "APPROVE"}
+
+
+ SequenceFlow_1i81jsh
+
+
+ ${approvalDecision == "REJECT"}
+
+
+ flow1
+ SequenceFlow_0on165o
+ SequenceFlow_1p4jixi
+
+
+
+ SequenceFlow_064ef2y
+ SequenceFlow_1wtga4h
+
+
+
+
+
+
+
+ SequenceFlow_0eqlrvy
+ SequenceFlow_1ccqf4c
+
+
+ SequenceFlow_0n52bjk
+ SequenceFlow_0a86c0j
+ SequenceFlow_0eqlrvy
+
+
+
+
+ SequenceFlow_0a86c0j
+
+
+
+ ${approvalDecision == "RETURN"}
+
+
+ SequenceFlow_0kbwukz
+ SequenceFlow_1wcuegh
+
+
+ SequenceFlow_1wcuegh
+ SequenceFlow_0on165o
+ SequenceFlow_142t3uc
+
+
+
+ ${amendAction == "RESUBMIT"}
+
+
+ SequenceFlow_132n50r
+
+
+ ${amendAction == "CANCEL"}
+
+
+ SequenceFlow_1aallpn
+ SequenceFlow_1eaf9ol
+
+
+ SequenceFlow_142t3uc
+ SequenceFlow_07zwntx
+ SequenceFlow_132n50r
+
+
+
+
+ SequenceFlow_07zwntx
+
+ PT5D
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessStages.kt b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessStages.kt
new file mode 100644
index 0000000..fb5a922
--- /dev/null
+++ b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessStages.kt
@@ -0,0 +1,54 @@
+package io.holunda.testing.examples.basic.junit5
+
+import com.tngtech.jgiven.annotation.As
+import com.tngtech.jgiven.annotation.BeforeStage
+import com.tngtech.jgiven.annotation.Quoted
+import io.holunda.camunda.bpm.extension.jgiven.JGivenProcessStage
+import io.holunda.camunda.bpm.extension.jgiven.ProcessStage
+import io.holunda.testing.examples.basic.ApprovalProcessBean
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Expressions.APPROVE_REQUEST_TASK_LISTENER
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Expressions.DETERMINE_APPROVAL_STRATEGY
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Expressions.LOAD_APPROVAL_REQUEST
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Variables.APPROVAL_DECISION
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Variables.APPROVAL_STRATEGY
+import io.holunda.testing.examples.basic.BasicProcessApplication
+import io.toolisticon.testing.jgiven.step
+import org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.assertThat
+import org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.externalTask
+import org.camunda.bpm.engine.test.mock.Mocks
+import org.camunda.bpm.engine.variable.Variables
+import org.camunda.bpm.engine.variable.Variables.putValue
+import org.camunda.bpm.extension.mockito.CamundaMockito.getJavaDelegateMock
+import org.camunda.bpm.extension.mockito.CamundaMockito.registerJavaDelegateMock
+
+@JGivenProcessStage
+class ApprovalProcessActionStage : ProcessStage() {
+
+ @BeforeStage
+ fun mock_all_delegates() {
+ registerJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY)
+ registerJavaDelegateMock(LOAD_APPROVAL_REQUEST)
+ // register a real listener
+ Mocks.register(APPROVE_REQUEST_TASK_LISTENER, BasicProcessApplication().approveRequestTaskListener())
+ }
+
+ fun process_is_started_for_request(@Quoted approvalRequestId: String) = step {
+ processInstanceSupplier = ApprovalProcessBean(camunda)
+ processInstanceSupplier.start(approvalRequestId)
+ assertThat(processInstanceSupplier.processInstance).isNotNull
+ assertThat(processInstanceSupplier.processInstance).isStarted
+ }
+
+ @As("\$approvalStrategy approval strategy can be applied")
+ fun approval_strategy_can_be_applied(@Quoted approvalStrategy: String) = step {
+ getJavaDelegateMock(DETERMINE_APPROVAL_STRATEGY).onExecutionSetVariables(Variables.putValue(APPROVAL_STRATEGY, approvalStrategy))
+ }
+
+ fun automatic_approval_returns(approvalResult: String) = step {
+ external_task_is_completed(externalTask().topicName, putValue(APPROVAL_DECISION, approvalResult))
+ }
+
+}
+
+@JGivenProcessStage
+class ApprovalProcessThenStage : ProcessStage()
diff --git a/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessTest.kt b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessTest.kt
new file mode 100644
index 0000000..8b3d6c2
--- /dev/null
+++ b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/ApprovalProcessTest.kt
@@ -0,0 +1,183 @@
+package io.holunda.testing.examples.basic.junit5
+
+import com.tngtech.jgiven.annotation.ScenarioState
+import com.tngtech.jgiven.junit5.ScenarioTest
+import io.holunda.testing.examples.basic.ApprovalProcessBean
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Elements
+import io.holunda.testing.examples.basic.ApprovalProcessBean.Expressions
+import io.toolisticon.testing.jgiven.AND
+import io.toolisticon.testing.jgiven.GIVEN
+import io.toolisticon.testing.jgiven.THEN
+import io.toolisticon.testing.jgiven.WHEN
+import org.camunda.bpm.engine.test.Deployment
+import org.camunda.bpm.engine.variable.Variables.putValue
+import org.camunda.bpm.extension.junit5.test.ProcessEngineExtension
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.api.extension.RegisterExtension
+import java.time.Period
+import java.util.*
+
+@ExtendWith(ProcessEngineExtension::class)
+@Deployment(resources = [ApprovalProcessBean.RESOURCE])
+internal class ApprovalProcessTest :
+ ScenarioTest() {
+
+ @RegisterExtension
+ val extension = TestProcessEngine.DEFAULT
+
+ @ScenarioState
+ val camunda = extension.processEngine
+
+ @Test
+ internal fun `should deploy`() {
+ THEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ }
+
+ @Test
+ internal fun `should start asynchronously`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ WHEN
+ .process_is_started_for_request(approvalRequestId)
+ THEN
+ .process_waits_in(Elements.START)
+ }
+
+ @Test
+ internal fun `should wait for automatic approve`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
+
+ WHEN
+ .process_continues()
+
+ THEN
+ .process_waits_in(Elements.SERVICE_AUTO_APPROVE)
+ .AND
+ .external_task_exists("approve-request")
+
+ }
+
+ @Test
+ internal fun `should automatic approve`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
+ .AND
+ .process_continues()
+
+ WHEN
+ .automatic_approval_returns(Expressions.ApprovalDecision.APPROVE)
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.SERVICE_AUTO_APPROVE, Elements.END_APPROVED)
+
+ }
+
+
+ @Test
+ internal fun `should automatically reject`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.AUTOMATIC)
+ .AND
+ .process_continues()
+
+ WHEN
+ .automatic_approval_returns(Expressions.ApprovalDecision.REJECT)
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.SERVICE_AUTO_APPROVE, Elements.END_REJECTED)
+
+ }
+
+ @Test
+ internal fun `should manually reject`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.MANUAL)
+ .AND
+ .process_continues()
+ .AND
+ .process_waits_in(Elements.USER_APPROVE_REQUEST)
+ .AND
+ .task_priority_is_between(10, 30)
+ .AND
+ .task_has_follow_up_date_after(Period.ofDays(1))
+
+ WHEN
+ .task_is_completed_with_variables(
+ putValue(ApprovalProcessBean.Variables.APPROVAL_DECISION, Expressions.ApprovalDecision.REJECT),
+ isAsyncAfter = true
+ )
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.END_REJECTED)
+
+ }
+
+ @Test
+ internal fun `should manually approve`() {
+
+ val approvalRequestId = UUID.randomUUID().toString()
+
+ GIVEN
+ .process_is_deployed(ApprovalProcessBean.KEY)
+ .AND
+ .process_is_started_for_request(approvalRequestId)
+ .AND
+ .approval_strategy_can_be_applied(Expressions.ApprovalStrategy.MANUAL)
+ .AND
+ .process_continues()
+ .AND
+ .process_waits_in(Elements.USER_APPROVE_REQUEST)
+
+ WHEN
+ .task_is_completed_with_variables(
+ putValue(ApprovalProcessBean.Variables.APPROVAL_DECISION, Expressions.ApprovalDecision.APPROVE),
+ isAsyncAfter = true
+ )
+
+ THEN
+ .process_is_finished()
+ .AND
+ .process_has_passed(Elements.END_APPROVED)
+
+ }
+}
diff --git a/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/TestProcessEngine.kt b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/TestProcessEngine.kt
new file mode 100644
index 0000000..cd62f9f
--- /dev/null
+++ b/examples/basic-junit5/src/test/kotlin/io/holunda/testing/examples/basic/junit5/TestProcessEngine.kt
@@ -0,0 +1,126 @@
+package io.holunda.testing.examples.basic.junit5
+
+import org.camunda.bpm.engine.ProcessEngine
+import org.camunda.bpm.engine.ProcessEngineConfiguration
+import org.camunda.bpm.engine.impl.ProcessEngineImpl
+import org.camunda.bpm.engine.impl.cfg.AbstractProcessEnginePlugin
+import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl
+import org.camunda.bpm.engine.impl.cfg.ProcessEnginePlugin
+import org.camunda.bpm.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration
+import org.camunda.bpm.engine.impl.history.HistoryLevel
+import org.camunda.bpm.engine.test.mock.MockExpressionManager
+import org.camunda.bpm.extension.junit5.test.ProcessEngineExtension
+import java.util.*
+import java.util.function.Consumer
+import java.util.stream.Collectors
+import java.util.stream.Stream
+
+
+enum class TestProcessEngine {
+ ;
+
+ class Builder internal constructor() {
+ private val configuration: ProcessEngineConfigurationImpl
+
+ init {
+ configuration = StandaloneInMemProcessEngineConfiguration()
+ configuration.historyLevel = HistoryLevel.HISTORY_LEVEL_FULL
+ configuration.databaseSchemaUpdate = ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE
+ configuration.isJobExecutorActivate = false
+ configuration.isDbMetricsReporterActivate = false
+ configuration.expressionManager = MockExpressionManager()
+ configuration.isTelemetryReporterActivate = false
+ configuration.isInitializeTelemetry = false
+ }
+
+ fun preInit(preInit: Consumer): Builder {
+ plugin(createPlugin(preInit, null, null))
+ return this
+ }
+
+ fun postInit(postInit: Consumer): Builder {
+ plugin(createPlugin(null, postInit, null))
+ return this
+ }
+
+ fun postProcessEngineBuild(postProcessEngineBuild: Consumer): Builder {
+ plugin(createPlugin(null, null, postProcessEngineBuild))
+ return this
+ }
+
+ fun withDefaultSerializationFormat(defaultSerializationFormat: String): Builder {
+ configuration.defaultSerializationFormat = defaultSerializationFormat
+ return this
+ }
+
+ fun plugin(plugin: ProcessEnginePlugin): Builder {
+ configuration.processEnginePlugins.add(plugin)
+ return this
+ }
+
+ fun withH2MemNameDefault(): Builder {
+ return withH2MemName("camunda")
+ }
+
+ fun withH2MemName(databaseName: String): Builder {
+ preInit {
+ it.jdbcUrl = String.format(
+ "jdbc:h2:mem:%s;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL;DATABASE_TO_LOWER=TRUE",
+ databaseName
+ )
+ }
+ return this
+ }
+
+ fun engine(): ProcessEngineImpl {
+ return configuration.buildProcessEngine() as ProcessEngineImpl
+ }
+
+ fun extension(): ProcessEngineExtension {
+ return ProcessEngineExtension.builder().useProcessEngine(engine()).build()
+ }
+
+ companion object {
+ private fun createPlugin(
+ preInit: Consumer?,
+ postInit: Consumer?,
+ postProcessEngineBuild: Consumer?
+ ): ProcessEnginePlugin {
+ return object : AbstractProcessEnginePlugin() {
+ override fun preInit(processEngineConfiguration: ProcessEngineConfigurationImpl) {
+ preInit?.accept(processEngineConfiguration)
+ }
+
+ override fun postInit(processEngineConfiguration: ProcessEngineConfigurationImpl) {
+ postInit?.accept(processEngineConfiguration)
+ }
+
+ override fun postProcessEngineBuild(processEngine: ProcessEngine) {
+ postProcessEngineBuild?.accept(processEngine)
+ }
+
+ override fun toString(): String {
+ return Stream.of(preInit, postInit, postProcessEngineBuild).map(Objects::nonNull)
+ .map { obj: Any -> obj.toString() }
+ .collect(Collectors.joining("-"))
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ // util class, final, no instance
+ val DEFAULT = builder()
+ .withDefaultSerializationFormat("application/json")
+ .extension()
+
+ fun builder(): Builder {
+ return Builder()
+ }
+
+ fun extension(): ProcessEngineExtension {
+ return builder().extension()
+ }
+ }
+}
diff --git a/examples/basic-junit5/src/test/resources/logback.xml b/examples/basic-junit5/src/test/resources/logback.xml
new file mode 100644
index 0000000..9e068a6
--- /dev/null
+++ b/examples/basic-junit5/src/test/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/basic/jgiven-reports/io.holunda.testing.examples.basic.ApprovalProcessTest.json b/examples/basic/jgiven-reports/io.holunda.testing.examples.basic.ApprovalProcessTest.json
deleted file mode 100644
index 9a89c68..0000000
--- a/examples/basic/jgiven-reports/io.holunda.testing.examples.basic.ApprovalProcessTest.json
+++ /dev/null
@@ -1,241 +0,0 @@
-{
- "className": "io.holunda.testing.examples.basic.ApprovalProcessTest",
- "name": "Approval Process",
- "scenarios": [
- {
- "className": "io.holunda.testing.examples.basic.ApprovalProcessTest",
- "testMethodName": "should manually reject",
- "description": "should manually reject",
- "tagIds": [],
- "explicitParameters": [],
- "derivedParameters": [],
- "casesAsTable": false,
- "scenarioCases": [
- {
- "caseNr": 1,
- "steps": [
- {
- "name": "process $ is deployed",
- "words": [
- {
- "value": "Given",
- "isIntroWord": true
- },
- {
- "value": "process"
- },
- {
- "value": "approval",
- "argumentInfo": {
- "argumentName": "processDefinitionKey",
- "formattedValue": "\"approval\""
- }
- },
- {
- "value": "is deployed"
- }
- ],
- "status": "PASSED",
- "durationInNanos": 14548485
- },
- {
- "name": "process is started for request",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "process is started for request"
- },
- {
- "value": "d22b5b69-98a2-45ab-95e3-d21b18918872",
- "argumentInfo": {
- "argumentName": "approvalRequestId",
- "formattedValue": "\"d22b5b69-98a2-45ab-95e3-d21b18918872\""
- }
- }
- ],
- "status": "PASSED",
- "durationInNanos": 82380
- },
- {
- "name": "$approvalStrategy approval strategy can be applied",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "MANUAL",
- "argumentInfo": {
- "argumentName": "approvalStrategy",
- "formattedValue": "\"MANUAL\""
- }
- },
- {
- "value": "approval strategy can be applied"
- }
- ],
- "status": "PASSED",
- "durationInNanos": 55716
- },
- {
- "name": "process continues",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "process continues"
- }
- ],
- "status": "PASSED",
- "durationInNanos": 47752
- },
- {
- "name": "process waits in $",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "process waits in"
- },
- {
- "value": "user_approve_request",
- "argumentInfo": {
- "argumentName": "activityId",
- "formattedValue": "\"user_approve_request\""
- }
- }
- ],
- "status": "PASSED",
- "durationInNanos": 46041
- },
- {
- "name": "task\u0027s priority is between $lower and $upper",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "task\u0027s priority is between"
- },
- {
- "value": "10",
- "argumentInfo": {
- "argumentName": "lower",
- "formattedValue": "10"
- }
- },
- {
- "value": "and"
- },
- {
- "value": "30",
- "argumentInfo": {
- "argumentName": "upper",
- "formattedValue": "30"
- }
- }
- ],
- "status": "PASSED",
- "durationInNanos": 49904
- },
- {
- "name": "task\u0027s follow-up date is $ after its creation",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "task\u0027s follow-up date is"
- },
- {
- "value": "P1D",
- "argumentInfo": {
- "argumentName": "followUpDatePeriod",
- "formattedValue": "P1D"
- }
- },
- {
- "value": "after its creation"
- }
- ],
- "status": "PASSED",
- "durationInNanos": 17661886
- },
- {
- "name": "task is completed with variables $",
- "words": [
- {
- "value": "When",
- "isIntroWord": true
- },
- {
- "value": "task is completed with variables"
- },
- {
- "value": "{\n approvalDecision \u003d\u003e Untyped value \u0027REJECT\u0027, isTransient \u003d false\n}",
- "argumentInfo": {
- "argumentName": "variables",
- "formattedValue": "{\n approvalDecision \u003d\u003e Untyped value \u0027REJECT\u0027, isTransient \u003d false\n}"
- }
- }
- ],
- "status": "PASSED",
- "durationInNanos": 67949846
- },
- {
- "name": "process is finished",
- "words": [
- {
- "value": "Then",
- "isIntroWord": true
- },
- {
- "value": "process is finished"
- }
- ],
- "status": "PASSED",
- "durationInNanos": 63320
- },
- {
- "name": "process has passed element(s) $",
- "words": [
- {
- "value": "and",
- "isIntroWord": true
- },
- {
- "value": "process has passed element(s)"
- },
- {
- "value": "end_rejected",
- "argumentInfo": {
- "argumentName": "elements",
- "formattedValue": "\"end_rejected\""
- }
- }
- ],
- "status": "PASSED",
- "durationInNanos": 51938513
- }
- ],
- "explicitArguments": [],
- "derivedArguments": [],
- "status": "SUCCESS",
- "durationInNanos": 994767867
- }
- ],
- "durationInNanos": 994767867,
- "executionStatus": "SUCCESS"
- }
- ],
- "tagMap": {}
-}
\ No newline at end of file
diff --git a/examples/basic/pom.xml b/examples/basic/pom.xml
index 02a7822..1a22d76 100644
--- a/examples/basic/pom.xml
+++ b/examples/basic/pom.xml
@@ -6,7 +6,7 @@
io.holunda.testing
camunda-bpm-jgiven-examples
- 0.0.8
+ 0.2.0
camunda-bpm-jgiven-examples-basic
@@ -24,7 +24,7 @@
org.camunda.bpm.extension.mockito
camunda-bpm-mockito
- 5.15.0
+ 5.16.0
test
@@ -36,6 +36,12 @@
org.camunda.bpm.springboot
camunda-bpm-spring-boot-starter-test
test
+
+
+ org.camunda.bpm.assert
+ camunda-bpm-assert-assertj3-11-1
+
+
org.camunda.bpm.assert
diff --git a/examples/basic/src/test/kotlin/io/holunda/testing/examples/basic/ApprovalProcessStages.kt b/examples/basic/src/test/kotlin/io/holunda/testing/examples/basic/ApprovalProcessStages.kt
index 6b2fe7c..4dc95c0 100644
--- a/examples/basic/src/test/kotlin/io/holunda/testing/examples/basic/ApprovalProcessStages.kt
+++ b/examples/basic/src/test/kotlin/io/holunda/testing/examples/basic/ApprovalProcessStages.kt
@@ -31,7 +31,7 @@ class ApprovalProcessActionStage : ProcessStage() {
- companion object {
- val processEngineRule: ProcessEngineRule = StandaloneInMemoryTestConfiguration().rule()
- }
-
@get: Rule
- @ScenarioState
- val camunda: ProcessEngineRule = processEngineRule
+ val rule: ProcessEngineRule = StandaloneInMemoryTestConfiguration().rule()
+ @ScenarioState
+ val camunda = rule.processEngine
@Test
fun `should deploy`() {
diff --git a/examples/pom.xml b/examples/pom.xml
index 5b35fda..15dcd52 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -6,18 +6,19 @@
io.holunda.testing
camunda-bpm-jgiven-parent
- 0.0.8
+ 0.2.0
camunda-bpm-jgiven-examples
pom
- 2.4.5
+ 2.6.4
basic
+ basic-junit5
diff --git a/extension/pom.xml b/extension/pom.xml
index 7735834..9713fc7 100644
--- a/extension/pom.xml
+++ b/extension/pom.xml
@@ -6,7 +6,7 @@
io.holunda.testing
camunda-bpm-jgiven-parent
- 0.0.8
+ 0.2.0
camunda-bpm-jgiven
@@ -18,6 +18,11 @@
jgiven-junit
${jgiven.version}
+
+ com.tngtech.jgiven
+ jgiven-junit5
+ ${jgiven.version}
+
com.tngtech.jgiven
jgiven-spring
diff --git a/extension/src/main/kotlin/io/holunda/camunda/bpm/extension/jgiven/ProcessStage.kt b/extension/src/main/kotlin/io/holunda/camunda/bpm/extension/jgiven/ProcessStage.kt
index 123903f..455c8dc 100644
--- a/extension/src/main/kotlin/io/holunda/camunda/bpm/extension/jgiven/ProcessStage.kt
+++ b/extension/src/main/kotlin/io/holunda/camunda/bpm/extension/jgiven/ProcessStage.kt
@@ -7,12 +7,15 @@ import io.holunda.camunda.bpm.extension.jgiven.formatter.QuotedVarargs
import io.holunda.camunda.bpm.extension.jgiven.formatter.VariableMapFormat
import io.toolisticon.testing.jgiven.step
import org.assertj.core.api.Assertions.*
+import org.camunda.bpm.engine.ProcessEngine
+import org.camunda.bpm.engine.impl.persistence.entity.TimerEntity
+import org.camunda.bpm.engine.impl.util.ClockUtil
import org.camunda.bpm.engine.runtime.ProcessInstance
-import org.camunda.bpm.engine.test.ProcessEngineRule
import org.camunda.bpm.engine.test.assertions.bpmn.BpmnAwareTests.*
import org.camunda.bpm.engine.variable.VariableMap
import org.camunda.bpm.engine.variable.Variables.createVariables
import java.time.Period
+import java.time.temporal.ChronoUnit
import java.util.*
import java.util.function.Supplier
@@ -56,7 +59,7 @@ class ProcessStage, PROCESS_BEAN : Suppl
* Process engine to work on.
*/
@ExpectedScenarioState(required = true)
- lateinit var camunda: ProcessEngineRule
+ lateinit var camunda: ProcessEngine
/**
* Instance supplier.
@@ -74,6 +77,58 @@ class ProcessStage, PROCESS_BEAN : Suppl
assertThat(processInstanceSupplier.get()).isWaitingAt(activityId)
}
+ /**
+ * Checks if the process instance is waiting in all activities with specified ids.
+ * @param activityId definition id.
+ * @return fluent stage.
+ */
+ @As("process waits in activities $")
+ fun process_waits_in(@QuotedVarargs vararg activityId: String) = step {
+ require(activityId.isNotEmpty()) { "At least one activity id must be provided" }
+ activityId.map {
+ val job = job(it)
+ assertThat(job).`as`("Expecting the process to be waiting in activity '$it', but it was not.").isNotNull
+ }
+ }
+
+ /**
+ * Checks if the process instance is waiting to receive all events in activities with specified ids.
+ * @param activityId definition id.
+ * @return fluent stage.
+ */
+ fun process_waits_for(@QuotedVarargs vararg activityId: String) = step {
+ require(activityId.isNotEmpty()) { "At least one activity id must be provided" }
+ val activityIdOfActiveEventSubscriptions =
+ runtimeService().createEventSubscriptionQuery().list().map { it.activityId }
+ assertThat(activityIdOfActiveEventSubscriptions)
+ .`as`(
+ "Expecting the process to wait for events in activity ${
+ activityId.joinToString(", ")
+ }, but it was waiting in ${
+ activityIdOfActiveEventSubscriptions.joinToString(", ")
+ }"
+ )
+ .containsExactlyInAnyOrder(*activityId)
+ }
+
+ /**
+ * Executes current job.
+ * @return fluent stage.
+ */
+ @As("process continues")
+ fun process_continues(): SELF = step {
+ job_is_executed()
+ }
+
+ /**
+ * Executes jobs named by the activities the current execution is waiting at.
+ * @param activityId activities the execution is waiting at.
+ * @return fluent stage.
+ */
+ @As("process continues")
+ fun process_continues(vararg activityId: String) = job_is_executed(*activityId)
+
+
/**
* Asserts that the process is deployed.
* @param processDefinitionKey process definition key.
@@ -88,7 +143,6 @@ class ProcessStage, PROCESS_BEAN : Suppl
.latestVersion()
.singleResult()
).isNotNull
-
}
/**
@@ -240,11 +294,23 @@ class ProcessStage, PROCESS_BEAN : Suppl
* @param variableName name of the variable.
* @return fluent stage.
*/
- @As("variables $ are not present")
- fun variable_is_not_present(@QuotedVarargs vararg variableName: String): SELF = step {
+ @As("variable $ is not present")
+ fun variable_is_not_present(@QuotedVarargs variableName: String): SELF = step {
assertThat(processInstanceSupplier.get())
.`as`("variable $variableName should not be present")
- .variables().doesNotContainKeys(*variableName)
+ .variables().doesNotContainKeys(variableName)
+ }
+
+ /**
+ * Asserts that variable are not set.
+ * @param variableNames names of the variables.
+ * @return fluent stage.
+ */
+ @As("variables $ are not present")
+ fun variables_are_not_present(@QuotedVarargs variableNames: Array): SELF = step {
+ assertThat(processInstanceSupplier.get())
+ .`as`("variables ${variableNames.joinToString(", ")} should not be present")
+ .variables().doesNotContainKeys(*variableNames)
}
/**
@@ -275,7 +341,7 @@ class ProcessStage, PROCESS_BEAN : Suppl
assertThat(processInstanceSupplier.get())
.`as`("Expecting the task to be marked as async after and continue on complete.")
.isWaitingAt(taskDefinitionKey)
- execute(job())
+ job_is_executed(taskDefinitionKey)
}
}
@@ -289,6 +355,7 @@ class ProcessStage, PROCESS_BEAN : Suppl
/**
* Executes current job.
+ * Be careful, this method will fail if more than one job is in place.
* @return fluent stage.
*/
fun job_is_executed(): SELF = step {
@@ -297,13 +364,65 @@ class ProcessStage, PROCESS_BEAN : Suppl
}
/**
- * Executes current job.
+ * Executes the jobs waiting in activities provided.
+ * @param activityId id of the activity the job is waiting in.
* @return fluent stage.
*/
- @As("process continues")
- fun process_continues(): SELF = step {
- assertThat(processInstanceSupplier.get()).isNotNull
- execute(job())
+ fun job_is_executed(vararg activityId: String) = step {
+ require(activityId.isNotEmpty()) { "At least one activity id must be provided" }
+ activityId.map {
+ val job = job(it)
+ assertThat(job).`as`("Expecting the process to be waiting in activity '$it', but it was not.").isNotNull
+ execute(job)
+ }
+ }
+
+ /**
+ * Asserts that the instance is waiting for the timer to be executed on the expected time.
+ * @param timerActivityId activity id of the timer.
+ * @param expectedDate expected data truncated to minute.
+ * @return fluent stage.
+ */
+ fun timer_is_waiting_until(timerActivityId: String, expectedDate: Date) = step {
+
+ val truncatedToMinutes = Date.from(expectedDate.toInstant().truncatedTo(ChronoUnit.MINUTES))
+
+ val timerJobs = managementService()
+ .createJobDefinitionQuery()
+ .activityIdIn(timerActivityId)
+ .list()
+ .mapNotNull { jobDefinition ->
+ managementService()
+ .createJobQuery()
+ .jobDefinitionId(jobDefinition.id)
+ .singleResult()
+ }.filterIsInstance()
+
+ assertThat(timerJobs).`as`("Expected one instance waiting in $timerActivityId, but found ${timerJobs.size}.").hasSize(1)
+ assertThat(timerJobs[0]).hasDueDate(truncatedToMinutes)
+ }
+
+ /**
+ * Sets engine time to new value and triggers the timer job execution.
+ * @param timerActivityId activity id of timer event.
+ * @param targetTime time to set engine time to.
+ * @return fluent stage.
+ */
+ fun time_passes(timerActivityId: String, targetTime: Date) = step {
+ ClockUtil.setCurrentTime(targetTime)
+
+ val timerJobs = managementService()
+ .createJobDefinitionQuery()
+ .activityIdIn(timerActivityId)
+ .list()
+ .mapNotNull { jobDefinition ->
+ managementService()
+ .createJobQuery()
+ .jobDefinitionId(jobDefinition.id)
+ .singleResult()
+ }.filterIsInstance()
+ assertThat(timerJobs).`as`("Expected one instance waiting in $timerActivityId, but found ${timerJobs.size}.").hasSize(1)
+ job_is_executed(timerActivityId)
}
/**
@@ -376,14 +495,14 @@ class ProcessStage, PROCESS_BEAN : Suppl
// exactly one subscription
assertThat(
- camunda.processEngine.runtimeService
+ camunda.runtimeService
.createEventSubscriptionQuery()
.processInstanceId(processInstanceSupplier.get().processInstanceId)
.eventType("message")
.eventName(messageName).count()
).isEqualTo(1)
- camunda.processEngine.runtimeService
+ camunda.runtimeService
.createMessageCorrelation(messageName)
.setVariables(variables)
.correlate()
diff --git a/pom.xml b/pom.xml
index 908ff53..11e5693 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
io.holunda.testing
camunda-bpm-jgiven-parent
- 0.0.8
+ 0.2.0
pom
@@ -15,17 +15,16 @@
- 7.15.0
- 1.0.0
+ 7.16.0
+ 1.2.0
4.13.2
3.19.0
- 10.0.0
- 1.0.0
+ 13.0.0
+ 1.0.1
11
- 1.5.0
+ 1.6.10
true
- 2.0.3
${java.version}
${java.version}
${java.version}
@@ -124,7 +123,7 @@
org.apache.maven.plugins
maven-enforcer-plugin
- 3.0.0-M3
+ 3.0.0
enforce-maven
@@ -156,7 +155,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.8.1
+ 3.10.0
UTF-8
@@ -204,7 +203,7 @@
com.amashchenko.maven.plugin
gitflow-maven-plugin
- 1.16.0
+ 1.18.0
master
@@ -227,6 +226,9 @@
org.jetbrains.kotlin
${kotlin.version}
+ ${java.version}
+ 1.5
+ 1.5
spring
jpa
@@ -237,7 +239,6 @@
- ${java.version}
@@ -298,7 +299,7 @@
org.jetbrains.dokka
dokka-maven-plugin
- 1.4.32
+ 1.6.10
test
@@ -314,7 +315,7 @@
org.codehaus.mojo
build-helper-maven-plugin
- 3.2.0
+ 3.3.0
generate-sources
@@ -377,7 +378,7 @@
maven-deploy-plugin
- 3.0.0-M1
+ 3.0.0-M2
true
@@ -385,7 +386,7 @@
org.sonatype.plugins
nexus-staging-maven-plugin
- 1.6.8
+ 1.6.12
default-deploy