Home > Blockchain >  Override bean in test with non-mocked bean
Override bean in test with non-mocked bean

Time:01-13

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().)

  • Related