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();
}
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 ?
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;
}