Home > OS >  Spring Boot Mockito - @InjectMocks - How to mock selected dependencies only
Spring Boot Mockito - @InjectMocks - How to mock selected dependencies only

Time:11-20

I have a @Service called UserServiceImpl that depends on two other beans. One is the UserRepository bean and the other is a bean called SessionService.

My requirement is during tests of the UserServiceImpl class, I must be able to inject a mock of the SessionService dependency, but keep the UserRepository dependency as it is.

My service class that looks like this:

@Service
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private SessionService sessionService;
    
    
    @Override
    public User create(User user) {
        log.info("User Creation at Service");
        // ... Do some validations .. //

        // This needs to be mocked in Unit Tests
        String returnValue = sessionService.doSomethingThatIDontWantInTests(); 

        user.setInternalKey(returnValue);

        // .. Do some more Validations .. //

        return userRepository.save(user);
    }
}

Now, here is my Test Class:

@SpringBootTest
class UserServiceTest {
    
    @InjectMocks
    private UserServiceImpl userService;
    
    @Mock
    private SessionService sessionService;
    
    
    @Test
    void CreateUserTest() {
        Mockito.when(sessionService.doSomethingThatIDontWantInTests()).thenReturn("abcxyz123321");
        User user = new User();
        user.setName("John Doe");
        user.setEmail("[email protected]");
        User savedUser = userService.create(user);
        
        assertNotNull(savedUser.getUserId());

    }
}

When I run this test, Mockito successfully mocks the SessionService call. But, UserServiceImpl.createUser() still fails with the message saying:

java.lang.NullPointerException: Cannot invoke "com.myproject.data.repos.UserRepository.save(User)" because "this.userRepository" is null

Should I inject UserRepository also as a mock and use Mockito to mock the UserRepository.save() method?

I would like to mock only the SessionService dependency, and not the UserRepository dependency.

Is it something doable? If so, how? Please advise.

Thanks, Sriram

CodePudding user response:

You are mixing different testing styles.

Style 1 - spring integration test

This is when Spring Boot creates the beans in its context, and you inject them to your test class.

  • use @SpringBootTest
  • use @Autowired to inject beans to your test
  • use @MockBean to replace beans in Spring contexts by mocks

Style 2 - Unit test

This does not use Spring DI. In this style, it is typical to mock all dependencies.

  • use @ExtendWith(MockitoExtension.class)
  • annotate dependencies as @Mock
  • annotate SUT with @InjectMocks

Using real dependencies is also possible, but in that case you need to construct SUT manually - Mockito does not support partial injections.

Unit tests tend to be much more lightweight and less brittle, on the other hand integration tests cover a large chunk of the app.

Note that if UserRepository is a spring-data repo, it cant be created manually.

CodePudding user response:

You must use a @SpyBean (https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/SpyBean.html), which will inject the original bean but which you can verify the calls and arguments.

  • Related