简体   繁体   中英

Mockito when().thenReturn() returns always null

I have the following service:

@Service
@RequiredArgsConstructor
public class LimitService {

    private final CoreService coreService;
    private final AuditService audit;

    public Limit get(User user) {

            Limit limit = coreService.get(user);
            if (limit != null) {
                Optional.ofNullable(limit.getAvailable())
                    .map(String::valueOf)
                    .map(BigDecimal::new)
                    .map(availableValue -> availableValue.setScale(2, RoundingMode.HALF_DOWN))
                    .map(BigDecimal::doubleValue)
                .ifPresentOrElse(val -> limit.setAvailable(val), () -> limit.setAvailable(0d));
            }
            return limit;
    }
}

and the following associated test

@ExtendWith(MockitoExtension.class)
public class LimitServiceTest {

    @Mock
    private CoreService coreService;

    @Mock
    private AuditService audit;

    @InjectMocks
    private LimitService service;

    @BeforeEach
    public void init(){
        MockitoAnnotations.openMocks(this);
    }


    @Test
    public void shouldReturnNull(){
        assertThat(this.service.get(new User())).isNull();
    }

    @Test
    public void shouldSetToZero_whenNull(){
        User user = new User();
        Mockito.when(this.coreService.get(any(User.class)))
            .thenReturn(new Limit());
        assertThat(this.service.get(user)).extracting(Limit::getAvailable)
            .isEqualTo(0d);
    }
}


When I run the second test in debug mode, I can see that a mock is actually generated for CoreService but Mockito seems to ignore the when..thenReturn.

I've also tried to use eq(user) instead of any(User.class), but the result is the same. I've a similar test in another project and everything works fine. I can't find out why this is not working in this case..

@ExtendWith(MockitoExtension.class)
public class LimitServiceTest {

    @Mock
    private CoreService coreService;

    @Mock
    private AuditService audit;

    @InjectMocks
    private LimitService service;

    @BeforeEach
    public void init(){
        MockitoAnnotations.openMocks(this);
    }

    // ...

@InjectMocks runs first (executed by MockitoExtension ) and assigns the mock instances to the fields in your annotated LimitService field.

Later, openMocks creates a second set of mock instances and will assign them to the fields in your test (but not re-assign the service's fields). The service will still reference the mock instances created by the extension.

This can be verified by printing the identity hash codes of the fields before and after calling openMocks :

    @BeforeEach
    public void init(){
        System.err.println("@InjectMocks:");
        System.err.println(System.identityHashCode(coreService));
        System.err.println(System.identityHashCode(service.coreService));
        MockitoAnnotations.openMocks(this);

        System.err.println("openMocks(this):");
        System.err.println(System.identityHashCode(coreService));
        System.err.println(System.identityHashCode(service.coreService));
    }

(This requires temporarily making the LimitService#coreService field accessible). Example output:

@InjectMocks:
763677574
763677574
openMocks(this):
234857227
763677574

Calling when now sets up stubbing for the second mock instance, but your service does not know about this instance, because it only holds a reference to the first mock instance.

This and related problems are discussed in the answer to Why is my class not calling my mocked methods in unit test? .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM