Testing of ExecutionListeners with JUnit & Mockito

Hi all,

I tried to use various integration and unit testing options for our spring-boot based processes. I have first checked the resources Testing examples, Process testing and Assert API.

Now we can roughly test process paths and delegate class execution unit test. However, mocking of the attributes of the classes derived from ExecutionListener does not really work, which ends up with a NPE at some point in the execution.

Based on the following code snippets and BPMN views can anyone tell me, what could be the problem? or is it the camunda internal class instantiation that differ between Listeners and Delegates?

Class under test:

@Named("ActivationLinkListener")
@Component
public class ActivationLinkListener implements ExecutionListener {

  private static final Logger LOGGER = LoggerFactory.getLogger(ActivationLinkListener.class);

  @Autowired
  private ProcessAVariables processAVars;

  @Override
  public void notify(DelegateExecution delegateExecution) {
    processAVars.setVariableScope(delegateExecution);
    Integer resendCount = processAVars.getResendCounter();
    if (smsResend == null){
      smsResend = 0;
    }
    LOGGER.info("BV Registration ActivationLinkListener called. Retry : " + resendCount++);
    processAVars.setResendCounter(smsResend);
  }
}

Test Code:

@RunWith(PowerMockRunner.class)
@Deployment(resources = "bpmn/ProcessA.bpmn")
public class ActivationLinkListenerTest extends AbstractProcessEngineRuleTest {

  @Rule
  @ClassRule
  public static ProcessEngineRule engineRule = TestCoverageProcessEngineRuleBuilder.create().build();

  @Mock
  private ProcessAVariables processAVars;

  @InjectMocks
  private ActivationLinkListener activationLinkListener;

  @Before
  public void setUp() {
    // Initialize mocks created above
    MockitoAnnotations.initMocks(this);
    Mocks.register("activationLinkListener", activationLinkListener);
  }

  @After
  public void teardown() {
    // calculate coverage for all tests
    Mocks.reset();
    cleanDeployments();
  }

  @Test
  public void startProcessAtSendActivationLinkDelegate() {

    // Intermediate given & received
    Mockito.when(regProcessVars.getResendCounter()).thenReturn(0);

    // Process run at the right task
    runProcessAtActivationLinkListener();

    // Then ...
    Mockito.verify(regProcessVars).setResendCounter(1);
  }

  private void runProcessAtActivationLinkListener() {
    VariableMap variables = Variables.createVariables()
      .putValue(ProcessAConstants.RESEND_COUNT, 1)
      .putValue(ProcessAConstants.MAX_RESEND_LIMIT, 3);

	runtimeService().createProcessInstanceByKey("ProcessA")
        .startBeforeActivity("ExclusiveGateway_1kyhy7x")
        .setVariables(variables)
        .execute();
  }


ProcessA.bpmn (7.5 KB)

Thanks a lot in advance.

Cheers
Tunç

Hi @tunch,

maybe you’re missing the initialization of the engine:

  @Before
  public void setup() {
    init(rule.getProcessEngine());
  }

Hope this helps, Ingo

Thanks @Ingo_Richtsmeier but it did not help. I believe the process engine is initialized correctly. It starts and arrives to the listener class during testing as expected. I also see that in the test thread the mocks (ProcessAVars) are set and injected correctly to the activationLinkListener.

The mocked processAVars is still simply null when the process arrives to the context of activationLinkListener. Either Mockito cannot mock it due to some Camunda internal mechanisms, or Camunda forces a new instantiation of this class during process run.
It would be really good to understand why.

Hello,
I am having the same problem, the only explanation that i found on the net that Mockito is not able to inject certain type of mocks,
Did you find any solution for this problem please ?

Hi @Khadija,

I could not find a real solution to the problem. It was bascially not possible to mock the Listeners properly for me when you wanted to test a part of the process in isolation.

The workaround that I used instead was to unit test the concerning Listener in isolation, where passing variables was still possible. A functional test looks more or less like the following based on the example I provided in the question:

@RunWith(PowerMockRunner.class)
@Deployment(resources = "bpmn/process.bpmn")
public class ActivationLinkListenerTest extends AbstractProcessEngineRuleTest {

  @Rule
  @ClassRule
  public static ProcessEngineRule engineRule = TestCoverageProcessEngineRuleBuilder.create().build();

  @Mock
  private ProcessVariables processVars;

  @InjectMocks
  private ActivationLinkListener activationLinkListener;

  @Before
  public void setUp() {
	// Initialize mocks created above
	MockitoAnnotations.initMocks(this);
  }

  @After
  public void teardown() {
	// calculate coverage for all tests
	Mocks.reset();
	cleanDeployments();
  }


  @Test // Simple unit testing of the delegate code
  public void increment	ResendCounter(){

	// Given & received
	Mockito.when(regProcessVars.getResendCounter()).thenReturn(1);
	DelegateExecution delegateExecution = delegateExecutionFake().withProcessDefinitionId("NewProcess");
	regProcessVars.setVariableScope(delegateExecution);

	// When
	activationLinkListener.notify(delegateExecution);

	// Then ...
	Mockito.verify(regProcessVars).setResendCounter(2);
  }
}

Thank you for your reply actually i have added this class MockArtifactFactory and then this Config to my InMemProcessEngineConfig and it resolved my problem :

public class MockArtifactFactory extends DefaultArtifactFactory {

@Override
public <T> T getArtifact(Class<T> clazz) {
    T mockedDelegate = (T) Mocks.get(clazz.getCanonicalName());
    if (mockedDelegate == null) {
        return super.getArtifact(clazz);
    }
    return mockedDelegate;
}

}

config.setArtifactFactory(new MockArtifactFactory());