How to test inner orElseGet() of Optional Object with Mockito and Lombok.Builder

77 views Asked by At

I have a class which holds a single predicate function:

@RequiredArgsConstructor
public class IsSampleTrue implements SamplePredicateFactory {
    private final SampleValueCalculator sampleValCalc;
    private final SampleParameterManager<SampleParameter> sampleParameterMgr;
    private final Cache<String, SampleData> sampleCache;

    @Override
    public SamplePredicate buildSamplePredicate(SampleContext ctx) {

       // if ctx is missing the premade MainObj then we create our own manually
       MainObject mainObj = ctx.getMainObject.orElseGet(() -> {
           return MainObject.builder()
               .a(ctx.getA())
               .b(ctx.getB())
               .c(ctx.getC())
               .build();
          }
       );

       SampleValueObject sampleValueObj = sampleValueCalc.calcSampleValue(mainObj);
       double sampleValueX = sampleValueObj.getSampleValueX(); // get NullPointerException here

       if (sampleValueX > 100) {
          return sample -> false;
       }
       
       return sample -> {
          
            if (...) {
              return false;
            }

            return true;
       }
    }
}

Implementation of calcSampleValue() method in SampleValueCalculator class:

@RequiredArgsConstructor
public class SampleValueCalculator {

    private final SampleParameterManager sampleParameterManager;
    
    public SampleValueObject calcSampleValue(MainObj mainObj) {
        return SampleValueObject.builder()
            .sampleValueA(mainObj.getA() * sampleParameterManager.getParameter(SampleParameter.VALUE_A_FACTOR))
            .sampleValueB(mainObj.getB())
            .sampleValueC(mainObj.getC())
            .build();
    }
}

Now I have a unit test to test this class:

public class IsSampleTrueTest {
    private static final double SAMPLE_CONSTANT = 0.1;
    @Mock
    private SampleValueCalculator sampleValCalc;
    @Mock
    private SampleParameterManager sampleParameterManager;
    @Mock
    private Cache<String, SampleData> sampleCache;
    @Mock
    private SampleContext ctx;
    @Mock
    private MainObj mainObj;

    private IsSampleTrue predicateFunc;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
        when(sampleParameterManager.getParameter(SampleParameter.SAMPLE_CONSTANT_VALUE)).thenReturn(SAMPLE_CONSTANT);
        when(ctx.getC()).thenReturn(c);

        predicateFunc = new IsSampleTrue(sampleValCalc, sampleParameterManager, sampleCache);
    }

    @Test
    public void testMainObjAbsentFromContext() {
        SampleData abcd = mock(SampleData.class);
        when(sampleCache.get("abcd")).thenReturn(Optional.of(abcd));
        when(abcd.getSomeValue()).thenReturn(1.0);
        
        when(ctx.getMainObj()).thenReturn(Optional.empty());
        when(ctx.getA()).thenReturn("a");
        when(ctx.getB()).thenReturn("b");
        when(ctx.getC()).thenReturn("c");
        
        Sample s1 = setupSample(...);
        
        SampleValueObject sampleValueObj = SampleValueObject.builder()
            .sampleValueA(1.0)
            .build();
        when(sampleValueCalc.calcSampleValue(mainObj)).thenReturn(sampleValueObj);

        SamplePredicate samplePred = predicateFunc.buildSamplePredicate(ctx); // get NullPointerException here
        assertTrue(samplePred.test(sample));
    }

    @Test
    public void testMainObjPresent() {
        SampleData abcd = mock(SampleData.class);
        when(sampleCache.get("abcd")).thenReturn(Optional.of(abcd));
        when(abcd.getSomeValue()).thenReturn(1.0);
        
        when(ctx.getMainObj()).thenReturn(Optional.of(mainObj));
        
        Sample s1 = setupSample(...);
        
        SampleValueObject sampleValueObj = SampleValueObject.builder()
            .sampleValueA(1.0)
            .build();
        when(sampleValueCalc.calcSampleValue(mainObj)).thenReturn(sampleValueObj);

        SamplePredicate samplePred = predicateFunc.buildSamplePredicate(ctx);
        assertTrue(samplePred.test(sample));
    }
}

When I run testMainObjAbsentFromContext(), I'm getting a NullPointerException:

java.lang.NullPointerException
    at com.package.sample.IsSampleTrue.buildSamplePredicate(IsSamplePredicate.java:21)
    at com.package.IsSampleTrueTest.testMainObjAbsentFromContext(IsSampleTruePredicateTest.java:42)
    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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:52)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    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.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

I've commented next to the lines where I'm getting the NullException. I'm not super familiar with how Mockito works so any pointers would be appreciated. The unit tests where I have ctx.getMainObj() returns the premade MainObj such as testMainObjPresent() are passing.

1

There are 1 answers

1
tgdavies On BEST ANSWER

Your when(sampleValueCalc.calcSampleValue(mainObj)).thenReturn(sampleValueObj); will not be triggered, because you don't pass mainObj -- you pass the MainObject you create in buildSamplePredicate.

You can either use when(sampleValueCalc.calcSampleValue(any(MainObject.class)).thenReturn(sampleValueObj); or create a MainObjectFactory class and pass a mock implementation of that into the IsSampleTrue constructor, so that you can stub it to supply a known MainObject instance.

testMainObjPresent() works because in that case your mocked context returns the MainObject instance created in the test.