Closed ajeans closed 3 years ago
@ajeans yes.. that make sense to me, would you submit a PR for it?
I was asked to provide more information about how we mock zeebe for unit testing
Assuming a spring boot app that will need to rely on a ZeebeClient for some services, and the requirement to unit test the services with Zeebe Assuming that we want UT with no external dependency and fast (so no existing Zeebe gateway / broker, no test-container)
'io.zeebe.spring:spring-zeebe'
(NOT 'io.zeebe.spring:spring-zeebe-starter'
)'org.mockito:mockito-inline'
and 'org.springframework.boot:spring-boot-starter-test'
@EnableZeebeClient
@ZeebeDeployment(classPathResources = {"example.bpmn"})
@Import(ZeebeBrokerConfig.class)
@SpringBootApplication
Create a spring @Configuration
called ZeebeBrokerConfig
that will basically expose a ZeebeClientBuilder
in two different ways
@ConditionalOnProperty(
prefix = ZEEBE_GATEWAY_PREFIX,
name = ZEEBE_GATEWAY_MOCK_KEY,
havingValue = "false",
matchIfMissing = true)
@Bean
public ZeebeClientBuilder zeebeClientBuilder() {
LOGGER.info("Loading zeebe clients connected to a zeebe broker");
final ZeebeClientBuilderImpl builder = new ZeebeClientBuilderImpl();
builder.withProperties(extractPropertiesFromEnvironment());
LOGGER.info("Zeebe broker client configuration {}", builder.toString());
return builder;
}
@ConditionalOnProperty(
prefix = ZEEBE_GATEWAY_PREFIX,
name = ZEEBE_GATEWAY_MOCK_KEY,
havingValue = "true")
@Bean
public ZeebeClientBuilder mockZeebeClientBuilder() {
LOGGER.warn("Loading zeebe clients connected to MOCK");
return new MockZeebeClientBuilder();
}
ZeebeClientBuilderImpl
Code looks like the following for my use case, but is very (too) specific to the way you will declare sources of properties in your environment
private Properties extractPropertiesFromEnvironment() {
final Properties properties = new Properties();
for (final PropertySource<?> propertySource :
((StandardEnvironment) environment).getPropertySources()) {
if (propertySource instanceof EnumerablePropertySource) {
Arrays.stream(((EnumerablePropertySource) propertySource).getPropertyNames())
.forEach(
propertyName ->
properties.put(propertyName, propertySource.getProperty(propertyName)));
}
}
return properties;
}
MockZeebeClientBuilder
public class MockZeebeClientBuilder implements ZeebeClientBuilder {
[...]
// Lots of things you will need to override to `return this;`
[...]
// Implement the build() with enough mockito to get your app to start
@Override
public ZeebeClient build() {
final ZeebeClientImpl zeebeClient = BDDMockito.mock(ZeebeClientImpl.class); // :throwup:
final DeployWorkflowCommandStep1 workflowCommandStep1 =
BDDMockito.mock(DeployWorkflowCommandStep1.class);
BDDMockito.when(zeebeClient.newDeployCommand()).thenReturn(workflowCommandStep1);
final DeployWorkflowCommandStep1.DeployWorkflowCommandBuilderStep2 workflowCommandStep2 =
BDDMockito.mock(DeployWorkflowCommandStep1.DeployWorkflowCommandBuilderStep2.class);
BDDMockito.when(workflowCommandStep1.addResourceFromClasspath(BDDMockito.anyString()))
.thenReturn(workflowCommandStep2);
final ZeebeFuture<DeploymentEvent> future = BDDMockito.mock(ZeebeFutureDeployment.class);
BDDMockito.when(workflowCommandStep2.send()).thenReturn(future);
final DeploymentEvent deploymentEvent = BDDMockito.mock(DeploymentEvent.class);
BDDMockito.when(future.join()).thenReturn(deploymentEvent);
CreateWorkflowInstanceCommandStep1 workflowStep1 =
BDDMockito.mock(CreateWorkflowInstanceCommandStep1.class);
BDDMockito.when(zeebeClient.newCreateInstanceCommand()).thenReturn(workflowStep1);
CreateWorkflowInstanceCommandStep1.CreateWorkflowInstanceCommandStep2 workflowStep2 =
BDDMockito.mock(
CreateWorkflowInstanceCommandStep1.CreateWorkflowInstanceCommandStep2.class);
BDDMockito.when(workflowStep1.bpmnProcessId(BDDMockito.anyString())).thenReturn(workflowStep2);
CreateWorkflowInstanceCommandStep1.CreateWorkflowInstanceCommandStep3 workflowStep3 =
BDDMockito.mock(
CreateWorkflowInstanceCommandStep1.CreateWorkflowInstanceCommandStep3.class);
BDDMockito.when(workflowStep2.latestVersion()).thenReturn(workflowStep3);
ZeebeFuture<WorkflowInstanceEvent> zeebeFutureWorkflow =
BDDMockito.mock(ZeebeFutureWorkflow.class);
BDDMockito.doReturn(workflowStep3).when(workflowStep3).variables(BDDMockito.anyMap());
BDDMockito.when(workflowStep3.send()).thenReturn(zeebeFutureWorkflow);
WorkflowInstanceEvent workflowInstance = BDDMockito.mock(WorkflowInstanceEvent.class);
BDDMockito.when(zeebeFutureWorkflow.join(BDDMockito.anyLong(), BDDMockito.eq(TimeUnit.SECONDS)))
.thenReturn(workflowInstance);
return zeebeClient;
}
So this is quite convoluted at the moment. Hope this helps
This issue and the associated will only allow us to relax the dependency from mockito-inline
to mockito
@ajeans this should be included in the 0.26.0 release now in maven central.
@salaboy Yes that works fine, I am now on 0.26.2 and I ditched mockito-inline with a wide grin.
Thanks :+1:
Following 0d5c20d490394af4400297024ab8215ca15857ae and its use of the Interface rather than the Implementation, it is now possible to kill another direct Impl dependency in
ZeebeClientSpringConfiguration
From
To
As we currently mock zeebeClient with mockito for some of our unit tests, this would make mocking even easier for us (No need for
mockito-inline
)