Tiina
Tiina

Reputation: 4785

Why this mockito does not mock

This is the test class:

@MockBean
private UserRepository userRepository;

@Before
public void beforeClass() {
    String mobile;
    when(this.userRepository.findByMobile(Mockito.anyString())).thenAnswer(new Answer<User>() {
        @Override
        public User answer(InvocationOnMock invocation) throws Throwable {
            Object[] args = invocation.getArguments();
            return MockData.getUserByMobile((String) args[0]);
        }
    });
}

@Test
@WithUserDetails(value = MockData.CUSTOMER_USERNAME, userDetailsServiceBeanName = "customizedUserDetailsService")
public void testAdd() throws Exception {}

And this is the userDetails implementation:

@Autowired
private UserRepository userRepository;

@Override
@Transactional
public UserDetails loadUserByUsername(String username) {
    User user = (User) userRepository.findByMobile(username); // user is always null 

What I expect is when userRepository.findByMobile is called, it should call the getUserByMobile method defined in @Before. But obviously the Mockito config does not work OR userRepository fail to mock. What's wrong and how to solve it?

Upvotes: 0

Views: 140

Answers (1)

Tiina
Tiina

Reputation: 4785

UserRepository is used in userDetails implementation, and it needs to be injected into userDetails as described in this. However because XXRepository is in interface, so @InjectedMock cannot be used. Then classes become: Test class:

    @MockBean
    private UserService userService;

    @InjectMocks
    private CustomizedUserDetailsService customizedUserDetailsService;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
        when(this.userService.findByMobile(Mockito.anyString())).thenAnswer(new Answer<User>() {
            @Override
            public User answer(InvocationOnMock invocation) throws Throwable {
                Object[] args = invocation.getArguments();
                return MockData.getUserByMobile((String) args[0]);
            }
        });
    }

    @Test
    @WithUserDetails(value = MockData.CUSTOMER_USERNAME, userDetailsServiceBeanName = "customizedUserDetailsService") {}

And userDetails:

@Autowired
private UserService userService;

@Override
@Transactional
public UserDetails loadUserByUsername(String username) {
    User user = (User) userService.findByMobile(username);

I can see that the userService in userDetails is the same userService mocked in test class, however @Before method is called after the @WithUserDetails userDetails. So finally in order to achieve loading MockData user been, I think I have to create another userDetails just for UT. EDIT 2: Actually, I have tried it without @InjectMocks and using userService (originally I was using userRepository), it works too.

Upvotes: 2

Related Questions