Peter
Peter

Reputation: 33

Testing in Spring project - injecting repository into service with Mockito

I'm trying to understand how to use Mockito in a Spring project, but I'm a bit stuck with the following:

I'm about to test a service with a real (in-memory) repository. I'm mocking every other object that's being used by that service. If I understand correctly, annotating an object with @Mock will be mocked, @Spy will use a real object, and with @InjectMocks will kind of "autowire" these annotated objects. However, I think I misunderstood @Spy because the following code is not working (seems like a mock object is inserted, neither a DB interaction, neither a NullPointerException comes up):

@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceTests {

    @Spy
    @Autowired
    private UserRepository userRepository;

    @Mock
    private MessageService messageService;

    @InjectMocks
    private UserService userService = new UserServiceImpl();

    @Test
    public void testUserRegistration() {
        userService.registerUser("[email protected]");

        Assert.assertEquals("There is one user in the repository after the first registration.", 1, userRepository.count());
    }
}

If I remove the @Spy annotation and insert the repository in the service with reflection, it works:

ReflectionTestUtils.setField(userService, "userRepository", userRepository);

This seems inconvenient, and I'm pretty sure that @Spy is meant to do this. Can anyone point out what I'm missing/misunderstanding?

Edit: one more thing - with the @Spy annotation I'm not getting a NullPointerException on save, but it seems like a mock object is inserted instead of the real repository.

Edit 2: I think this is the answer for my confusion:

SpringBoot Junit bean autowire

Upvotes: 3

Views: 8879

Answers (1)

Woxis17
Woxis17

Reputation: 51

The problem is that you probably @Autowired some services/repositories in UserService like these:

public class UserServiceImpl() {

  @Autowired
  private UserRepository userRepository;
  @Autowired
  private MessageService messageService;

  // ... all the stuff
}

Mockito tries to inject mocks through constructor or setters which are not available. Try to redesign your class UserServiceImpl, so it has constructor like:

public class UserServiceImpl() {

  private final UserRepository userRepository;
  private final MessageService messageService;

  @Autowired
  public UserServiceImpl(UserRepository userRepository, MessageService messageService) {
    this.userRepository = userRepository;
    this.messageService = messageService;
  }

Then you can instantiate the service in @Before with the constructor using those mocks.

Upvotes: 2

Related Questions