Mocking Call Activities for the Camunda "scenario" tests

Hi all,

I wrote some code today which allows to write the following test code for running process instances with call activities without adding the called process models to the deployment (which is also possible, but now it’s also possible to sort of “mock” them) …

when(scenario.waitsAtMockedCallActivity("CallActivity")).thenReturn((callActivity)` -> 
    callActivity.complete();
);

Scenario.run(scenario)
    .withMockedProcess("Child")
    .startByKey("BoundaryInterruptingTimerTest")
    .execute();

verify(scenario, times(1)).hasStarted("CallActivity");
verify(scenario, times(1)).hasFinished("CallActivity");

This was a for a long time requested feature … so I thought it’s a good point to start with some new work towards Camunda Scenario Community Extension 2.0 or so? Because I have a few more ideas … :slight_smile:

Basically a mocked Call Activity behaves like a natural wait state, because it is internally mocked with an in-memory created process with an external task. This means you can do all things like asserting status like incoming variables, injecting variables for outgoing variables, throwing BPMN errors or deferring the completion of the call activity into the future … which might cause some timer events in the parent process or other processes associated to the scenario to trigger earlier than the call activity completion.

The feature is also already tested with all sorts of call activity stuff like error, timer boundary events etc. So I created a PR on the project and thought, maybe somebody wants to check it out and give some feedback? That’d be awesome! :sunglasses:

All the best,
Martin.

3 Likes

Nice job. Great for those larger model or dynamic call activity scenarios.

1 Like

Hi @martin.schimak

That is a great feature! Trying this on the Invoice Process I figured out that it only works if the called Process is in another file.

If not you get:

java.lang.AssertionError: Process 'ReviewInvoiceP' declared to be mocked, but it is already deployed. Please remove from your list of explicit deployments.

As I am working on a generic Testing it would be great, if these two cases could be handled equally.

What do you think, is this possible?

Hi @pme123! Glad you like it and can use it!

Interesting finding … the exception you see is ofc intentional, however I did not consider the case when you have several executable processes in one file. One could ofc consider not to throw any exception at all, but to overwrite the existing deployment with the mocked deployment and maybe log a warn or info.

… but can you elaborate a bit more what you mean and want to achieve with „generic testing“? What do you more specifically mean with treating the two cases „equally“? I would like to understand that better first. Thank you!

Hi @martin.schimak,

Thanks for the quick response. I work on an idea to develop Processes Domain Driven.
So you would define all the necessary Inputs and Outputs with Domain Models (Scala case classes to be specific).
With this you can then easily generate your documentation, tests, etc.

The project is still in an experimental state (see https://github.com/pme123/camundala if you are intersted)
I proposed a presentation for the Camunda Community Summit - so if accepted, there will be an introduction :slightly_smiling_face:.

So my scenario test looks like:

    test(InvoiceReceiptP)( // the process is started with its input
      approveInvoiceUT, // the UserTask will be completed with its output
      prepareBankTransferUT,
      archiveInvoiceST,  // the ServiceTask will be completed with its output
      InvoiceProcessedEE  // the EndEvent will be checked if the process finished as expected
    ) // the process will be tested with its output

This case works already :partying_face:

But as soon there is a Call Activitiy is involved I haven’t a solution yet:

    test(
      InvoiceReceiptP
        .withOut(InvoiceReceiptCheck(false))
    )(
      approveInvoiceUT
        .withOut(ApproveInvoice(false)),
      reviewInvoiceCA // the CallActivity will be completed with its output.
        .withOut(InvoiceReviewed(false)), 
    )

I hope this makes some sense :wink: