0

I've got a problem with mockito tests named "Cannot instantiate @InjectMocks field named 'userService' of type 'UserServiceImpl'. You haven't provided the instance at field declaration so I tried to construct the instance." I've been searching through stackoverflow but unfortunately I haven't found right solution for myself. So, I've got interface 'UserService', its implementation and several methods which I wanna tests.

UserService:

public interface UserService {
Optional<User> getByToken(String token);
PagingDto<User> getUsers(String filter, Integer startIndex, Integer count);}

UserServiceImpl:

public class UserServiceImpl extends AbstractPagingService<User, Integer, UserEntity> implements UserService {
private final UserRepository userRepository;

@Autowired
public UserServiceImpl(ProviderConfiguration providerConfiguration, UserRepository userRepository) {
    super(providerConfiguration.getFilter().getMaxResult(), userRepository, Sort.by("surname"));
    this.userRepository = userRepository;
}
four methods

Test class:

@ExtendWith(SpringExtension.class)
class UserServiceImplTest {
private static final String TOKEN = "token";

@InjectMocks
private UserServiceImpl userService;

@Spy
private UserRepository userRepository;

@Test
void testGetByToken() {
    final UserEntity userEntity = new UserEntity();
    final int id = 1;
    final User expectedUser = new User();
    final String email = "email@email.com";
    final String surname = "surname";
    final String name = "name";
    final String patronymic = "patronymic";

    userEntity.setId(id);

    expectedUser.setId(id);
    expectedUser.setEmail(email);
    expectedUser.setSurname(surname);
    expectedUser.setName(name);
    expectedUser.setPatronymic(patronymic);

    when(userRepository.findById(id)).thenReturn(Optional.of(userEntity));
    final User actualUser = userService.getByToken(TOKEN).orElseThrow();
    assertEquals(expectedUser, actualUser);
    assertEquals(email, userEntity.getEmail());
    assertEquals(surname, userEntity.getSurname());
    assertEquals(name, userEntity.getName());
    assertEquals(patronymic, userEntity.getPatronymic());
}

@Test
void testGetAbsentUserByToken() {
    assertFalse(userService.getByToken(TOKEN).isPresent());
}

@Test
void testEmptyUser() {
    when(userRepository.findById(any())).thenThrow(IllegalArgumentException.class);
    assertFalse(userService.getByToken(TOKEN).isPresent());
}

Everything is OK if I didn't extends AbstractPagingService<User, Integer, UserEntity>. But if I do there always be a mistake I mentioned above.

Jack
  • 33
  • 8
  • Before injecting mocks into `UserServiceImpl` have you tried to mock `ProviderConfiguration`, as it is autowired into `UserServiceImpl` constructor? – artiomi Oct 19 '22 at 19:05
  • Thanks for your feedback! Yep, I've tried but it has the same error and moreover, I'm not using ProviderConfiguration during my method testing – Jack Oct 19 '22 at 19:32

1 Answers1

0

You are missing a mock for ProviderConfiguration which is a required dependency for your service.

@ExtendWith(SpringExtension.class)
class UserServiceImplTest {
  private static final String TOKEN = "token";

  @InjectMocks
  private UserServiceImpl userService;

  @Spy
  private UserRepository userRepository;

  @Mock
  private ProviderConfiguration providerConfiguration;

Probably needs some more steps to be properly set up, including mocking getFilter() or by having the mock return mocks (generally discouraged).

If ProviderConfiguration is a data class without much logic, consider assigning a real config in your test:

@ExtendWith(SpringExtension.class)
class UserServiceImplTest {
  private static final String TOKEN = "token";

  private ProviderConfiguration providerConfiguration = createTestProviderConfiguration();

  @InjectMocks
  private UserServiceImpl userService;

  @Spy
  private UserRepository userRepository;

  private static ProviderConfiguration createTestProviderConfiguration() {
    final Filter filter = new Filter();
    filter.setMaxResult(42);

    final ProviderConfiguration cfg = new ProviderConfiguration();
    cfg.setFilter = filter;
    return cfg;
  }
knittl
  • 246,190
  • 53
  • 318
  • 364
  • knittl, thanks a lot for for your answer! But unfortunately it didn't help, I've tried previously to include ProviderConfiguration with @Mock annotation and included getFilter(). – Jack Oct 20 '22 at 08:41
  • @Jack "it didn't work" is not a useful error description. Why did it not work? Were there any errors? If yes, which ones? Or no errors, but the actual result was different from the expected one? How did you include `getFilter()`? – knittl Oct 20 '22 at 08:45
  • sorry, I haven't mentioned. I've got the same error with "Cannot instantiate @InjectMocks...". And I've added two mocks: private ProviderConfiguration providerConfiguration; and private ProviderConfiguration.Filter filter. – Jack Oct 20 '22 at 09:33
  • @Jack any exception? I'd imagine that `getFilter()` returns null, thus throwing an NPE in the `super` call of the constructor. Perhaps you want to assign a real configuration in your test? – knittl Oct 20 '22 at 09:35
  • I've added two additional lines: when(filter.getMaxResult()).thenReturn(100); when(providerConfiguration.getFilter()).thenReturn(filter); – Jack Oct 20 '22 at 09:37
  • @Jack those methods won't be called before the Mockito runner instantiates, assigns, and injects the mocks. I suggest reading [Why is my class not calling my mocked methods in unit test?](https://stackoverflow.com/q/74027324/112968), which gives a good overview about common misconceptions when setting up mocks. – knittl Oct 20 '22 at 09:39