Kokki
Kokki

Reputation: 83

MeterRegistry counter test case failing

I have implemented Micrometer Prometheus counter in my service by injecting MeterRegistry and incrementing the count as shown below, and I have written a test case as well, but when I am running the test case, I am getting:

"java.lang.NullPointerException: Cannot invoke "io.micrometer.core.instrument.MeterRegistry.counter(String, String[])" because "this.meterRegistry" is null".

Service file:

@Autowired
private MeterRegistry meterRegistry;
    
public void counterIncrement() { 
    meterRegistry.counter("test_count").increment();
}

Test case file:

@MockBean
private MeterRegistry registry;
     
@Test
void testCounter() {
    //  invoking counterIncrement();
}

Upvotes: 4

Views: 15225

Answers (4)

Stephen Flavin
Stephen Flavin

Reputation: 158

You can test metrics without Mockito using SimpleMeterRegistry

  @Test
  void testCounter() {
    var meterRegistry = new SimpleMeterRegistry();
    Metrics.addRegistry(meterRegistry);

    //  invoke counterIncrement();

    var actual = meterRegistry.counter("test_count").count();
    
    assertEquals(1.0d, actual);
  }

Upvotes: 8

Hans-Christian
Hans-Christian

Reputation: 687

How do you create your class under test? Since the registry is never instantiated, something seems up with how you setup your test.

Check that you are using the @MockBean in the correct way. This will replace the bean in the application context and if you do not spin up a spring context in your test, it will not work. See this post for more info.

A different approach would be to use @Mock and inject the registry in the constructor, example:

@ExtendWith(MockitoExtension.class)
public class MyServiceTest {

  @Mock
  private MeterRegistry registry;

  private MyService myService;

  @BeforeEach
  void setup() {
    myService = new MyService(registry);
  }

  @Test
  void testCounter() {
    var counter = mock(Counter.class);
    given(registry.counter(any(String.class))).willReturn(counter);
    myService.counterIncrement();
  }

Upvotes: 2

Sascha Doerdelmann
Sascha Doerdelmann

Reputation: 846

Depending on the test and the service there are several ways to deal with the missing MeterRegistry.

  1. If you use a spring context in your test, try to use a test configuration to create the MeterRegistry bean.
  2. If your test uses some Mock framework, you could mock the MeterRegistry as suggested by by @Hans-Christian.
  3. If you simply make the member meterRegistry non-private. You could set it to a SimpleMeterRegistry in some setup method, anotated with @BeforeEach as suggested by @checketts in the comments.
  4. If mocking the meter registry gets complicated, you could easily build and use some factory that provides the registry and mock this factory. A very easy factory will do, e.g. a spring @Component with an autowired MeterRegistry and some public getter for the factory.
  5. You could use the factory method pattern as described in wikipedia to get the MeterRegistry, overwrite the factory method in a subclass of your service and use this subclass in the test. (Note that the gang of four did use a static factory method, you'll need a non-static method.)

I favour solution 3 but would use solution 1 whenever appropriate. I've added solutions 4 and 5 just because there might be some additional reasons and special cases that make these solutions a good choice. If so, I prefer 4 over 5.

Upvotes: 0

Daniel Jacob
Daniel Jacob

Reputation: 1536

Depending on which junit version you are using you need to add the annotation to your test class. Junit 5: @ExtendWith(MockitoExtension.class) or for Junit 4: @RunWith(MockitoJUnitRunner.class)

Upvotes: 0

Related Questions