Search code examples
spring-bootprometheusgrafanapromql

Spring Autowire bean NullPointerException during Groovy tests but works fine during Runtime


I am using the prometheus library for getting metrics of my Spring Boot application (REST API). I am using the library io.prometheus.simpleclient:0.4.0 and I am including it in my Maven pom.xml. I am using the Counter and @Autowiring (I've tried both field and constructor injection) it to one of my own classes, like such

MyCustomMetricsClass.java

@Component
public MyCustomMetricsClass {
    @Autowire
    private Counter counterBean;

   public void myOwnMetricsMethod() {
       counterBean.inc();
       // do some stuff
    }

THEN, I am @Autowiring this MyCustomMetricsClass into my Service class, MyServiceClass.java, where it seems to run fine when I run my API locally using Spring Boot embedded tomcat on port 8080 (localhost:8080). I can hit endpoints and the metrics are being reported correctly at the actuator endpoint (localhost:8080/actuator/metrics). e.g.

MyServiceClass.java

public MyServiceClass {
    @Autowire
    private MyCustomMetricsclass myMetrics;


    public void genericServiceMethod() {
        myMetrics.MyOwnMetricsMethod(); // NULL POINTER EXCEPTION ONLY DURING TEST SCOPE (GROOVY)
    }

The problem is, when I run mvn install, which triggers the local Groovy unit tests I have written, I keep getting a NULL POINTER EXCEPTION. With the debugger, I can debug the Groovy unit tests and see in my Service class, the myMetrics variable is NULL. But I don't understand why it works fine at runtime, also, I have annotated the MyCustomMetricsClass as a @Component annotation, so it should be a bean being scanned by Spring Component scan.

This is a multi-module project ; with the structure below

my-project (root, contains root pom.xml)
   - my-api (module, contains RestController. has its own pom.xml)
   - my-service (module. contains service classes, has its own pom.xml)
   - my-model (module, contains all POJO/DTO model classes, has its own pom.xml)

Am I missing some dependency on my classpath? Why does it work at runtime but not during tests? (all my dependencies should have default scope) Is the autowiring broken?


Solution

  • Can you share the code from your unit test?

    At a guess, you're using a mocking framework, maybe Mockito?

    If this assumption is true, remember, your unit test won't be running up the full Spring context, so no auto-wiring will take place. You will need to inject mocks for the autowired components.

    e.g.:

    @RunWith(MockitoJUnitRunner.class)
    public class MyServiceClassTest {
    
      @Mock
      private MyCustomMetricsClass myCustomMetricsClass; 
    
      @InjectMocks
      private MyServiceClass myServiceClass;
    
      @Test
      public void shouldDoTesting() {
    
        myServiceClass. genericServiceMethod();
    
        verify(myMetrics).MyOwnMetricsMethod();
    
      }
    }