I want to unit test a class that uses a named spring bean of a List, but I can't figure out how to override the bean with my own test bean filled with mocks.
The class that defines the named bean:
@Configuration
@RequiredArgsConstructor
public class JmsReceiveConfig {
@Bean(name = "FooConsumers")
List<JmsConsumer> messageConsumers(QueuedReceiveService fooMessageListener) throws Exception {
List<JmsConsumer> list = new ArrayList<>();
for (MyJmsProperties.Broker broker : myJmsProperties.getBrokers()) {
JmsConsumer jmsConsumer =
messageConsumer(broker, myJmsProperties.getKeystore(), myJmsProperties.getKeystorePassword(),
myJmsProperties.getTruststore(), myJmsProperties.getTruststorePassword(), fooMessageListener);
list.add(jmsConsumer);
}
return list;
}
...
}
My class that uses the named bean:
@Component
@ConditionalOnEnabledHealthIndicator("barIndicator")
@RequiredArgsConstructor
public class MyHealthIndicator implements HealthIndicator {
@Inject
@Qualifier("FooConsumers")
private List<JmsConsumer> jmsConsumersList;
@Override
public Health health() {
int connectedCount = 0;
Map<String, Object> detailsMap = new HashMap<>();
for (JmsConsumer consumer : jmsConsumersList) {
if (consumer.isConnected()) {
connectedCount ;
}
detailsMap.put(consumer.getAlias(), consumer.isConnected());
}
return Health.up()
.withDetails(detailsMap)
.build();
}
}
I tried this test class:
@ExtendWith(SpringExtension.class)
public class MyHealthIndicatorTest {
private MyHealthIndicator myHealthIndicator;
@Mock
JmsConsumer jmsConsumer1;
@Mock
JmsConsumer jmsConsumer2;
@Bean(name = "FooConsumers")
List<JmsConsumer> messageConsumers(QueuedReceiveService fooMessageListener) throws Exception {
ArrayList<JmsConsumer> jmsConsumers = new ArrayList<>();
jmsConsumers.add(jmsConsumer1);
jmsConsumers.add(jmsConsumer2);
return jmsConsumers;
}
@BeforeEach
public void setup() throws Exception {
myHealthIndicator = new MyHealthIndicator();
}
@Test
public void testStatusUpAll() {
Mockito.when(jmsConsumer1.getAlias())
.thenReturn("jmsConsumer1");
Mockito.when(jmsConsumer1.isConnected())
.thenReturn(true);
Mockito.when(jmsConsumer2.getAlias())
.thenReturn("jmsConsumer2");
Mockito.when(jmsConsumer2.isConnected())
.thenReturn(true);
Health healthCheck = myHealthIndicator.health();
assertEquals(healthCheck.getStatus(), Status.UP);
}
}
I expected the named bean I defined in the test class to be used in the @Test
method inside the myHealthIndicator.health()
call, but instead when that call runs, the jmsConsumers list within it is null, giving a NullPointerException on the for (JmsConsumer consumer : jmsConsumersList)
line.
I also tried using a mock JmsReceiveConfig so I could change my @BeforeEach
to:
List<JmsConsumer> jmsConsumers = new ArrayList<>();
jmsConsumers.add(jmsConsumer1);
jmsConsumers.add(jmsConsumer2);
Mockito.when(jmsReceiveConfig.messageConsumers(Mockito.any(QueuedReceiveService.class)))
.thenReturn(jmsConsumers);
radItsHealthIndicator = new RadItsHealthIndicator();
but I still get the same NPE exception in the same spot.
CodePudding user response:
Don't bother using SpringExtension
, just use MockitoExtension
.
Give MyHealthIndicator
a constructor which sets jmsConsumersList
.
So setup
becomes:
@BeforeEach
public void setup() throws Exception {
ArrayList<JmsConsumer> jmsConsumers = new ArrayList<>();
jmsConsumers.add(jmsConsumer1);
jmsConsumers.add(jmsConsumer2);
myHealthIndicator = new MyHealthIndicator(jmsConsumers);
}
The rest of your test can stay the same (of course you don't need messageConsumers()
.)