Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Mockito argThat in a loop results in NullPointerException

One of the legacy tests uses Mockito argThat to stub corresponding results in a loop.

It is passing if I decrease the number of iterations to 1 (numberOfItems = 1, i.e. like if there's no loop).

As soon as it gets to the 2nd iteration, it errors out with NPE:

java.lang.NullPointerException: Cannot invoke "getValue()" because "arg" is null

The code is like this:

 @Test
 public void testMy() {
     int numberOfItems = 2;
     List<MyData> myDataList = getTestData(numberOfItems);
     
     for (MyData md : myDataList) {
         when(mockedService.someMethod(
                argThat(arg -> Objects.equals(arg.getValue(), md.getMyValue())))) // NPE here on arg.getValue()
             .thenReturn(getExpectedResult(md.getMyValue()));
     }
     
     List<MyData> resData = testService.methodUnderTest(myDataList);
     
     // assert... resData list has all the expected items and values
 }

The method being tested is similar to the following

public List<MyData> methodUnderTest(List<MyData> myDataList) {
    myDataList.forEach(myData -> {
            // ... some logic ...
        
            myData.setResult(mockedService.someMethod(new MyRequest(myData.getMyValue())));
        }
    });

    return myDataList;
}

But for the error case the method call is not even reached, coz NPE happens earlier in the unit test code.

Originally it was passing being used somewhat like this

when(mockedService.someMethod(
       argThat(hasProperty("value", equalTo(md.getMyValue())))))
    .thenReturn(getExpectedResult(md.getMyValue()));

however, I saw a recommendation to prefer comparing the objects directly, so I tried to re-write the stubbing as suggested, getting rid of those equalTo and hasProperty, bot now got into those NPE error :)

Any tips on what am I doing wrong, please? Thanks!!

like image 586
RAM237 Avatar asked Nov 04 '25 08:11

RAM237


2 Answers

When you call when(mockedService.someMethod(argThat(arg -> Objects.equals(arg.getValue(), md.getMyValue())))).thenReturn(getExpectedResult(md.getMyValue())); the first time, the method becomes stubbed. When you call it a second time, the existing stub tries to match the second stubbing call. argThat (and all other matcher methods) return null, which makes the matcher of your existing stub throw.

Two potential solutions:

  • Change your matcher to argThat(arg -> arg != null && ...) to not match null inputs
  • Don't use the when(mock.call(argThat(...))).thenReturn(result) form – which actually calls the method! –, but switch to doReturn(result).when(mock).call(argThat(...))
like image 108
knittl Avatar answered Nov 06 '25 22:11

knittl


Was able to kind of solve it going from the other end.

Long story short, it all started after I upgraded to Java 17 and Mockito to 5.3.1, this made argThat incompatible with hamcrest hasProperty due to those Matcher / ArgumentMatcher incompatibility (https://stackoverflow.com/a/8351055/2518705).

At this point I decided to replace those hamcrest matchers and faced the NPE.

Now I realized, I can make it work just by using org.mockito.hamcrest.MockitoHamcrest.argThat instead of org.mockito.ArgumentMatchers.argThat.

Meanwhile, the original question potentially could still have the separate correct answer :)

like image 36
RAM237 Avatar answered Nov 06 '25 21:11

RAM237



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!