Unit testing workflows with external tasks


Hi, trying to set up unit tests for a given set of workflows that use mostly external tasks. The aim is testing the workflows’ principal function and error behaviour, in a completely mocked in-memory environment.

To emulate the external task client, I use a separate thread polling for tasks like this:

List<LockedExternalTask> tasks = externalTaskService.fetchAndLock(…).topic(…).execute();
for (final LockedExternalTask task : tasks) {
    externalTaskService.complete(task.getId(), …);

Here’s the complete, simplified code with a small model of three steps, two synchronous service tasks and one external task in between:

With no external tasks (ie, if you comment out line 38) everything runs fine and the process instance ends (line 60).

With the external task, the process instance does not end:

INFO  [pool-1-thread-1 ] MockWorker polling ...
INFO  [main            ] In SignalListener: received start
INFO  [main            ] In Step1, trace=initial
INFO  [pool-1-thread-1 ] MockWorker polling ...
INFO  [pool-1-thread-1 ] In MockWorker Step2, trace=initial -> Step1
INFO  [pool-1-thread-1 ] In Step3, trace=initial -> Step1 -> Step2
INFO  [pool-1-thread-1 ] In SignalListener: received end
INFO  [pool-1-thread-1 ] MockWorker polling ...
INFO  [pool-1-thread-1 ] MockWorker polling ...
INFO  [main            ] ExecutorService shut down
INFO  [pool-1-thread-1 ] MockWorker caught java.lang.InterruptedException
INFO  [pool-1-thread-1 ] Exiting MockWorker

Expected :true
Actual   :false

I also notice that all steps after the external task are running in the second thread, not in the main thread which makes me wonder:

Is this the right pattern? Is it allowed and safe to do this kind of multi-threaded access to the engine?

Why is it that the process instance is not ended after Step3 is executed?

Extra question: Is there a way to get notified by the engine when a process ends without explicitly modelling the end event?



Hi @ujay68,

When you test your process, you can easily mock your exernal task by completing it in the test code. This library helps you:https://github.com/camunda/camunda-bpm-assert

Here you can write

complete(task("myExternalTask"), withVariables("var1", "value1"));

and don’t care about multithreading.

Extra answer: One way in BPMN is to model a message end event: https://docs.camunda.org/manual/7.10/reference/bpmn20/events/message-events/#message-end-event. But it requires some code to send the message.

Also, the last service exection from the external task client can inform you about this. As externalTaskService.complete() runs synchronus until new the process state is saved in the database, the process instance is ended when the call returns.

Hope this helps, Ingo