11
votes

When I enable Actuators in my Spring Boot application, my Junit tests failed with a NullPointer Exception. The actuators are working fine, and the tests were working fine before I added the actuator artifact in my pom.xml file.

Seems like the

@MockBean
MailSender mailSender;

is causing the problem

My pom.xml file:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

My Test:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureTestDatabase
public class MailServiceTest extends CommonServiceTest {

    @Autowired
    MailService mailService;

    @MockBean
    MailSender mailSender;

    private static final String TO = "[email protected]";
    private static final String SUBJECT = "Subject";
    private static final String TEXT = "Text";
    private static final String TOKEN = "1234";

    @Value("${app.email.from}")
    private String fromEmail;

    @Value("${app.email.support}")
    private String supportEmail;

    private ArgumentCaptor<SimpleMailMessage> simpleMailMessageCaptor;

    @Override
    public void initObjects() {
        simpleMailMessageCaptor = ArgumentCaptor.forClass(SimpleMailMessage.class);
    }

    @Override
    public void initMocks() {

    }

    @Test
    public void sendMailTest() {

        mailService.sendMail(TO, SUBJECT, TEXT);

        verify(mailSender).send(simpleMailMessageCaptor.capture());

        assertEquals(TO, simpleMailMessageCaptor.getValue().getTo()[0]);
        assertEquals(SUBJECT, simpleMailMessageCaptor.getValue().getSubject());
        assertEquals(fromEmail, simpleMailMessageCaptor.getValue().getFrom());
        assertEquals(TEXT, simpleMailMessageCaptor.getValue().getText());
    }

}

The error:

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
    at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:189)
    at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:131)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mailHealthIndicator' defined in class path resource [org/springframework/boot/actuate/autoconfigure/HealthIndicatorAutoConfiguration$MailHealthIndicatorConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthIndicator]: Factory method 'mailHealthIndicator' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1128)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1023)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:111)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 29 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthIndicator]: Factory method 'mailHealthIndicator' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588)
    ... 46 more
Caused by: java.lang.NullPointerException
    at org.springframework.boot.actuate.autoconfigure.CompositeHealthIndicatorConfiguration.createHealthIndicator(CompositeHealthIndicatorConfiguration.java:42)
    at org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration$MailHealthIndicatorConfiguration.mailHealthIndicator(HealthIndicatorAutoConfiguration.java:334)
    at org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration$MailHealthIndicatorConfiguration$$EnhancerBySpringCGLIB$$25b5897.CGLIB$mailHealthIndicator$0(<generated>)
    at org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration$MailHealthIndicatorConfiguration$$EnhancerBySpringCGLIB$$25b5897$$FastClassBySpringCGLIB$$da72ce26.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:356)
    at org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration$MailHealthIndicatorConfiguration$$EnhancerBySpringCGLIB$$25b5897.mailHealthIndicator(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 47 more
4
similar issue when executing unit test that references a class from another dependency.borys86
I am experiencing the same problem with the JavaMailSender class and Actuator.Felipe

4 Answers

10
votes

The only solution I found was to add a properties in the test itself:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureTestDatabase
// Otherwise I have a NullPointer, don't know why
@TestPropertySource(
        properties = {"management.health.mail.enabled=false"})
public class MailServiceTest extends CommonServiceTest {
3
votes

In addition to Leamas suggestion I would like to add, that this case can occur if you have used @MockBean to mock the MailSender, in this case it seems that the MockedMailSender is not found by the MailHealthIndicator. This was the case for me and disabeling the MailHealthIndicator using Leamas suggestion worked.

0
votes

This is usually a symptom of having some proxy class wrapping your JavaMailSenderImpl.

Check that you use interfaces in your dependencies e.g. JavaMailSender and you'll see that HealthIndicatorAutoConfiguration.MailHealthIndicatorConfiguration finds and populates mailSenders:

public MailHealthIndicatorConfiguration(
        ObjectProvider<Map<String, JavaMailSenderImpl>> mailSenders) {
    this.mailSenders = mailSenders.getIfAvailable();
}
0
votes

You can add below properties in application-test.properties, it will disable default configurations

eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.enabled=false