Hot questions for Using Mockito in spring security

Top 10 Java Open Source / Mockito / spring security

Question:

I'm aware this question gets asked a lot, but maybe I have some things that are particular to this. I'm trying to do some integration tests on a Spring Boot application that supports REST (not Spring MVC) and for some reason SecurityContextHolder.getContext().getAuthentication() always returns null, even when using @WithMockUser on the test. I'm not certain if this has to do with using profiles on the configuration classes, but so far we haven't had troubles with this.

Class
@Override
public ResponseEntity<EmployeeDTO> meGet() {
    Principal principal = SecurityContextHolder.getContext().getAuthentication();
    logger.debug("Endpoint called: me({})", principal);
    EmployeeDTO result;

    // Get user email from security context
    String email = principal.getName(); // NPE here

// ...
}
Test
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        properties = {"eureka.client.enabled:false"})
@WithMockUser
@ActiveProfiles(value = "test")
public class MeControllerTest extends IntegrationSpringBootTest {

@Autowired
private TestRestTemplate restTemplate;

@MockBean
private SecurityContext securityContext;

@MockBean
private Authentication authentication;

@MockBean
private EmployeeRepository employeeRepository;

@BeforeClass
public static void setUp() {

}

@Before
@Override
public void resetMocks() {
    reset(employeeRepository);
}

@Test
public void meGet() throws Exception {
    when(securityContext.getAuthentication()).thenReturn(authentication);
    securityContext.setAuthentication(authentication);
    when(authentication.getPrincipal()).thenReturn(mockEmployee());
    SecurityContextHolder.setContext(securityContext);
    when(employeeRepository.findByEmail(anyString())).thenReturn(mockEmployee());

    ResponseEntity<EmployeeDTO> employeeDTOResponseEntity =
            this.restTemplate.getForEntity("/me", EmployeeDTO.class);
// ...
}

If I return a mock Principal instead of mockEmployee() the test cannot even start because this happens:

org.springframework.beans.factory.BeanCreationException: Could not inject field: private org.springframework.security.core.Authentication com.gft.employee.controller.MeControllerTest.authentication; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'org.springframework.security.core.Authentication#0' is expected to be of type 'org.springframework.security.core.Authentication' but was actually of type '$java.security.Principal$$EnhancerByMockitoWithCGLIB$$657040e6'

Additional clarifications: This Spring Boot app also uses OAuth2 for authorization, but it must be turned off for these tests. That's why we use profiles. Omitting the @ActiveProfiles annotation gives us a 401 Unauthorized error against the endpoint request.

I could use PowerMock but I would like to avoid it if possible.


Answer:

Easier Way of writing Junit for Authentication SecurityContextHolder would be to mock them. Following is the working implementation of it. You can add the mock classes as per your need and then set context of SecurityContextHolder and then use when() to further mock and return proper mock value.

    AccessToken mockAccessToken = mock(AccessToken.class);
    Authentication authentication = mock(Authentication.class);
    SecurityContext securityContext = mock(SecurityContext.class);

    when(securityContext.getAuthentication()).thenReturn(authentication);

    SecurityContextHolder.setContext(securityContext);

    when(SecurityContextHolder.getContext().getAuthentication().getDetails()).thenReturn(mockSimpleUserObject);

Question:

I tried to create tests for my method in UserServiceImpl class:

@Override
    public User getActualUser() throws WebSecurityException {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (!(authentication instanceof AnonymousAuthenticationToken)) {
            return userRepository.findByLogin(authentication.getName());
        }
        throw new WebSecurityException("Authenticated user not found");
    }

but I always get NullPointerException even if my authentication is instance of AnonymousAuthenticationToken, tests always goes to return userRepository.findByLogin(authentication.getName()); line.

This is my test class:

@RunWith(MockitoJUnitRunner.class)
public class UserServiceImplTest {

    @InjectMocks
    UserServiceImpl userService;

    @Mock
    UserRepository userRepository;

@Test(expected = WebSecurityException.class)
    public void testGetActualUserWhenAuthenticationIsInstanceOfAnonymousAuthenticationToken() {

        //SETUP
        SecurityContext securityContext = mock(SecurityContext.class);
        Authentication authentication = mock(AnonymousAuthenticationToken.class);
        when(securityContext.getAuthentication()).thenReturn(authentication);

        //CALL
        userService.getActualUser();
    }

    @Test
    public void testGetActualUserWhenAuthenticationIsNotInstanceOfAnonymousAuthenticationToken() {

        //SETUP
        User user = new User();
        Authentication authentication = mock(Authentication.class);
        when(userRepository.findByLogin(anyString())).thenReturn(user);
        when(authentication.getName()).thenReturn("user");

        //CALL
        userService.getActualUser();

        //TODO VERIFY
    }

}

Could you create proper tests for this method?


Answer:

You forgot to:

SecurityContextHolder.setContext(/*mock*/securityContext);

... in your test setup, this will fix the primary NullPointerException!

Question:

Could someone tell me how should I test those two methods:

public boolean deleteUser(Principal principal) {
    if (findLoggedInUser(principal) != null) {
        userRepository.delete(findLoggedInUser(principal));
        return true;
    }
    return false;
}

public User findLoggedInUser(Principal principal) {
    return findUserbyUsername(principal.getName());
}

The problem is that I'm using currently logged in user with basic authentication and don't know how, and if I can mock those Principals. Is there a way to do that? Those methods are in my Service layer so maybe I can't do unit tests and I'm left with integration tests because those methods heavily use DB?

EDIT 1: My changed test class:

public class UserServiceBeanTest {

@Spy
@InjectMocks
private UserServiceBean userServiceBean;

@Mock
private UserRepository userRepository;

@Mock
private Principal principal;

@Mock
private PasswordEncoder passwordEncoder;

@Mock
private User userStub;

private String defaultName = "user";
private  String defaultPassword = "password";
private String defaultEmail = "example@example.com";

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
}


@Test
public void shouldReturnTrue_whenUserDeleted() {
    //given
    when(principal.getName()).thenReturn(defaultName);
    when(userServiceBean.findLoggedInUser(principal)).thenReturn(userStub);

    // when
    boolean removed = userServiceBean.deleteUser(principal);

    //then
    assertTrue(removed);
    verify(userRepository, times(1));
}

@Test
public void shouldReturnFalse_whenUserNotFound() {
    //given
    when(principal.getName()).thenReturn(defaultName);
    when(userServiceBean.findLoggedInUser(principal)).thenReturn(null);

    //when
    boolean removed = userServiceBean.deleteUser(principal);

    //then
    assertFalse(removed);
    verify(userRepository, times(0));
}

}

I'm getting those errors now:

org.mockito.exceptions.misusing.UnfinishedVerificationException: 

Missin

g method call for verify(mock) here:
-> at com.doublemc.services.UserServiceBeanTest.shouldReturnTrue_whenUserDeleted(UserServiceBeanTest.java:63)

Example of correct verification:
    verify(mock).doSomething()

Also, this error might show up because you verify either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.


    at com.doublemc.services.UserServiceBeanTest.init(UserServiceBeanTest.java:48)

EDIT 2: Here is my UserServiceBean class:

package com.doublemc.services;

import com.doublemc.domain.ToDoItem;
import com.doublemc.domain.User;
import com.doublemc.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.security.Principal;

@Service
@Transactional
public class UserServiceBean {

private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;

@Autowired
UserServiceBean(UserRepository userRepository, PasswordEncoder passwordEncoder) {
    this.userRepository = userRepository;
    this.passwordEncoder = passwordEncoder;
}

public User saveUser(User user) {
    User newUser = new User();
    newUser.setUsername(user.getUsername());
    newUser.setEmail(user.getEmail());
    newUser.setPassword(passwordEncoder.encode(user.getPassword()));
    return userRepository.save(newUser);
}

public boolean userExists(User user) {
    return userRepository.findByUsername(user.getUsername()) != null;
}

public Iterable<ToDoItem> getAllToDoItems(User user) {
    return user.getToDoItems();
}

public boolean deleteUser(Principal principal) {
    if (findLoggedInUser(principal) != null) {
        userRepository.delete(findLoggedInUser(principal));
        return true;
    }
    return false;
}

public User findLoggedInUser(Principal principal) {
    return userRepository.findByUsername(principal.getName());
}

}

Here is my UserRepository:

public interface UserRepository extends CrudRepository<User, Long> {
User findByUsername(String username);
}

EDIT 6: I created myself three more tests:

@Test
public void shouldReturnUser_whenPassedUser() {
    // given
    when(userRepository.save(any(User.class))).thenReturn(new User(defaultName, defaultPassword, defaultEmail));

    // when
    User savedUser = userServiceBean.saveUser(userStub);

    // then
    assertNotNull(savedUser);
    verify(userRepository, times(1)).save(any(User.class));
}

@Test
public void shouldReturnTrue_whenUserExists() {
    // given
    when(userStub.getUsername()).thenReturn(defaultName);
    when(userRepository.findByUsername(userStub.getUsername())).thenReturn(userStub);

    // when
    boolean exists = userServiceBean.userExists(userStub);

    // then
    assertTrue(exists);
    verify(userRepository, times(1)).findByUsername(defaultName);
}

@Test
public void shouldReturnFalse_whenUserNotFoundByUsername() {
    // given
    when(userStub.getUsername()).thenReturn(defaultName);
    when(userRepository.findByUsername(userStub.getUsername())).thenReturn(null);

    // when
    boolean exists = userServiceBean.userExists(userStub);

    // then
    assertFalse(exists);
    verify(userRepository, times(1)).findByUsername(defaultName);
}

And here are tested methods: UserServiceBean.saveUser:

public User saveUser(User user) {
    User newUser = new User(user.getUsername(), user.getEmail(), passwordEncoder.encode(user.getPassword()));
    return userRepository.save(newUser);
}

UserServiceBean.userExists:

public boolean userExists(User user) {
    return userRepository.findByUsername(user.getUsername()) != null;
}

Answer:

This is how i would do it (Junit + Mockito).

There are two tests cases in the given example.

Btw.. i think you could do a little refactoring as you are (i guess) hitting the database twice:

public boolean deleteUser(Principal principal) {
    User loggedUser = findLoggedInUser(principal);
    if (loggedUser != null) {
        userRepository.delete(loggedUser);
        return true;
    }
    return false;
}

To the tests..

import static org.mockito.Mockito.*;
import mypkg.Service;
import mypkg.User;
import mypkg.UserRepository;
import org.junit.Assert;
import org.junit.Before;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;

import java.security.Principal;

public class ServiceTest {

    @Spy
    @InjectMocks
    private Service service;

    @Mock
    private UserRepository userRepository;

    @Mock
    private Principal principal;

    @Mock
    private User userStub;

    private String defaultName = "name";

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

   @org.junit.Test
   public void shouldReturnTrue_whenUserDeleted() throws Exception{
       // Arrange
       when(principal.getName()).thenReturn(defaultName);
       when(service.findUserbyUsername(defaultName)).thenReturn(userStub);

       // Act
       boolean removed = service.deleteUser(principal);

       // Assert
       Assert.assertTrue(removed);
       verify(userRepository, times(1)).delete(userStub);
   }

    @org.junit.Test
    public void shouldReturnFalse_whenUserNotFound() throws Exception{
        // Arrange
        when(principal.getName()).thenReturn(defaultName);
        when(service.findUserbyUsername(defaultName)).thenReturn(null);

        // Act
        boolean removed = service.deleteUser(principal);

        // Assert
        Assert.assertFalse(removed);
        verify(userRepository, times(0)).delete(userStub);
    }
}

The biggest thing to get out of this is that you Mock / Stub any outside dependencies (UserRepository in this case) and focus only on the logic contained in that service method. What is going inside that delete is not relevant to the test.. all you care about is whether that method has been invoked with a certain parameter.. and thats it.

Let me know if all is clear.. i will explain if needed.

Update

@InjectMocks is a convenient method of injecting dependencies into the class that you are going to test. The injection happens by setter/constructor or as a last resort by reflection.

In the above example as the Service class has the UserRepository dependency and there is a @Mock defined:

@Mock
private UserRepository userRepository;

Mockito will inject that into the Service.

@Spy is like @Mock, except that it allows you to selectively mock only certain bahvior and by default the real implementation is invoked.

In my case i used it to mock the findUserbyUsername method of the Service as what is going inside is not important in our two tests:

when(service.findUserbyUsername(defaultName)).thenReturn(userStub);

Question:

When I run unit test on the controller by mocking the service bean, it looks like the service method is not called at all. Is this expected behvaior or am I missing something?

SearchController.java

@Controller
public class SearchController {

    @Autowired
    SearchService searchService;

    @RequestMapping(value="/search", method=RequestMethod.GET)
    public String showSearchPage(Model model){

        model.addAttribute("list", searchService.findAll());

        return "search";
    }

}

SearchControllerTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/springapp-servlet.xml")
public class SearchControllerTest {

    @Autowired
    private WebApplicationContext webAppContext;

    private MockMvc mockMvc;

    private SearchService searchServiceMock;

    @Before
    public void setUp(){
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webAppContext).build();

        this.searchServiceMock = Mockito.mock(SearchServiceImpl.class);
    }

    @Test
    public void testShowSearchPage() throws Exception{

        when(searchServiceMock.findAll())
        .thenReturn(Arrays.asList("abc", "acb", "123"));

        this.mockMvc.perform(get("/search.do"))
        .andExpect(status().isOk())
        .andExpect(view().name("search"))
        .andExpect(forwardedUrl("/WEB-INF/jsp/search.jsp"))
        .andExpect(model().attribute("list", hasSize(3)));

        verify(searchServiceMock, times(1)).findAll(); //this test is failing

        verifyNoMoreInteractions(searchServiceMock);
    }
}

When I run the test, it seems like findAll() method isn't getting called and it is throwing exception. "Wanted but not invoked searchServiceImpl.findAll()"

What mistake am I making here?

-------------Update------------------

SearchControllerTestNew.java

public class SearchControllerTestNew {

    @InjectMocks
    SearchController searchController;

    @Mock
    SearchService searchServiceMock;

    @Mock
    View mockView;

    MockMvc mockMvc;

    @Before
    public void setUp(){

        MockitoAnnotations.initMocks(this);

        mockMvc = MockMvcBuilders.standaloneSetup(searchController).setSingleView(mockView)
                .build();
    }

    @Test
    public void testShowSearchPage() throws Exception{



        when(searchServiceMock.findAll())
        .thenReturn(Arrays.asList("abc", "acb", "123"));


        this.mockMvc.perform(get("/search.do"))
        .andExpect(status().isOk())
        .andExpect(view().name("search"))
        .andExpect(model().attribute("list", hasSize(3)))
        .andExpect(forwardedUrl("/WEB-INF/jsp/search.jsp"));//this fails now

        verify(searchServiceMock, times(1)).findAll();

        verifyNoMoreInteractions(searchServiceMock);
    }
}

Answer:

Your SearchController will use the autowired SearchService (part of your application context) not your mock, notice you create a mock but you are not using it anywhere, instead you create a MockMvc based on your application context.

A solution would be, using a standalone setup which gives you full control over the instantiation and initialization of controllers and their dependencies:

@Before
public void setUp(){
  this.searchServiceMock = Mockito.mock(SearchServiceImpl.class);
  mockMvc = MockMvcBuilders.standaloneSetup(new SearchController(seachServiceMock))
    .setViewResolvers(viewResolver())
    .build();
}

private ViewResolver viewResolver() {
  InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();

  viewResolver.setPrefix("/WEB-INF/jsp/");
  viewResolver.setSuffix(".jsp");

  return viewResolver;
}

I've omitted some MVC infrastructure configuration in this example check the MockMvcBuilder documentation for further configuration.

Question:

I can test my JwtTokenAuthenticationFilter class. How can I write the test cases of this class using Mockito & JUnit? I can Only test this class.

I don't understand how I can mock the class.

public class JwtTokenAuthenticationFilter extends  OncePerRequestFilter {

    private final JwtConfig jwtConfig;

    public JwtTokenAuthenticationFilter(JwtConfig jwtConfig) {
        this.jwtConfig = jwtConfig;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        System.out.println("Code is reachable");
        // 1. get the authentication header. Tokens are supposed to be passed in the authentication header
        String header = request.getHeader(jwtConfig.getHeader());
        // 2. validate the header and check the prefix
        if (header == null || !header.startsWith(jwtConfig.getPrefix())) {
            chain.doFilter(request, response);
            return;// If not valid, go to the next filter.
        }
        // If there is no token provided and hence the user won't be authenticated. 
        // It's Ok. Maybe the user accessing a public path or asking for a token.

        // All secured paths that needs a token are already defined and secured in config class.
        // And If user tried to access without access token, then he won't be authenticated and an exception will be thrown.

        // 3. Get the token
        String token = header.replace("Bearer","");

        try {    // exceptions might be thrown in creating the claims if for example the token is expired

            // 4. Validate the token
            Claims claims = Jwts.parser()
                    .setSigningKey(jwtConfig.getSecret().getBytes())
                    .parseClaimsJws(token)
                    .getBody();

            String username = claims.getSubject();

            if (username != null) {
                @SuppressWarnings("unchecked")
                List<String> authorities = (List<String>) claims.get(ApplicationConstant.tokenAuthorities);
                List<GrantedAuthority> grantAuthorities = new ArrayList<GrantedAuthority>();

                // 5. Create auth object
                // UsernamePasswordAuthenticationToken:A built-in object, used by spring to represent the current authenticated / being authenticated user.
                // It needs a list of authorities, which has type of GrantedAuthority interface, where SimpleGrantedAuthority is an implementation of that interface

                for (String authName : authorities) {
                    grantAuthorities.add(new SimpleGrantedAuthority(authName));
                }
                UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
                        username, null, grantAuthorities);
                // 6. Authenticate the user
                // Now, user is authenticated
                SecurityContextHolder.getContext().setAuthentication(auth);
            }
        } catch (Exception e) {
            // In case of failure. Make sure it's clear; so guarantee user won't be authenticated
            SecurityContextHolder.clearContext();
        }

        // go to the next filter in the filter chain
        chain.doFilter(request, response);
    }
}

Answer:

Spring gives you some mocks:

See the org.springframework.mock.web package for other mocks.


And here's some code that can help you to get started:

@RunWith(SpringRunner.class)
public class JwtTokenAuthenticationFilterTest {

    @Before
    public void before() {
        SecurityContextHolder.clearContext();
    }

    @After
    public void after() {
        SecurityContextHolder.clearContext();
    }

    @Test
    @SneakyThrows
    public void doFilterInternal_shouldPopulateSecurityContext_whenTokenIsValid() {

        String token = issueTokenForUser("john.doe");
        MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
        request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);

        MockHttpServletResponse response = new MockHttpServletResponse();
        FilterChain filterChain = new MockFilterChain();
        FilterConfig filterConfig = new MockFilterConfig();

        JwtTokenAuthenticationFilter filter = new JwtTokenAuthenticationFilter();
        filter.init(filterConfig);
        filter.doFilter(request, response, filterChain);
        filter.destroy();

        assertThat(SecurityContextHolder.getContext().getAuthentication())
                .satisfies(authentication -> {
                    assertThat(authentication).isNotNull();
                    assertThat(authentication.getName()).isEqualTo("john.doe");
                });
    }

    private String issueTokenForUser(String username) {
        return "xxxxx.yyyyy.zzzzz"; // Implement as per your needs
    }
}

The above code uses AssertJ for assertions.

Question:

I'm trying to mock authentication in Java for an authentication. This is my test class for mocking the code:

    MockitoAnnotations.initMocks(this);

    mvc = MockMvcBuilders.standaloneSetup(this.controller).build();

    final List<AuthenticationProvider> providers = mock(ArrayList.class);
    final AbstractUserDetailsAuthenticationProvider provider = mock(
            AbstractUserDetailsAuthenticationProvider.class);
    when(provider.supports(any(Class.class))).thenReturn(false);
    when(providers.size()).thenReturn(1);
    session = new MockHttpSession();
    when(providers.get(anyInt())).thenReturn(provider);
    when(request.getSession()).thenReturn(session);
    when(request.getSession(false)).thenReturn(session);
    when(providers.iterator()).thenReturn(new Iterator<AuthenticationProvider>() {
        private int currentIndex = 0;

        @Override
        public AuthenticationProvider next() {

            return providers.get(currentIndex++);
        }

        @Override
        public boolean hasNext() {
            return currentIndex < providers.size() && providers.get(currentIndex) != null;
        }
    });
    SingleProviderAuthenticationManager manager = new SingleProviderAuthenticationManager(providers);
    Map<String, AuthenticationManager> map = new HashMap<String, AuthenticationManager>();
    map.put("db", manager);

    filter.setAuthenticationManagerMap(map);
    when(request.getMethod()).thenReturn("POST");
    when(request.getParameter("username")).thenReturn("admin");
    when(request.getParameter("password")).thenReturn("admin");

    List<User> users = new ArrayList<User>();
    User user = new User();
    user.setSourceSystem("db");
    users.add(user);
    when(userService.getUserReferenceByUsername("admin")).thenReturn(users);
    auth = filter.attemptAuthentication(request, response);

Now, on the line where I put the manager in the map.put() method, when I put "db" it actually gives the provider manager as null and I get the NullPointerException in the ProviderManager.

    for (AuthenticationProvider provider : getProviders()) {
        if (!provider.supports(toTest)) {
            continue;
        }

Even though I've tested the same thing in my main code with the providers I pass its still showing a NullPointerException. And if I put "ldap" instead it gives me a NullPointerException in the UsernamePasswordAuthenticationFilter here: (last line where the return happens)

        password = "";
    }

    username = username.trim();

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

    // Allow subclasses to set the "details" property
    setDetails(request, authRequest);

    return this.getAuthenticationManager().authenticate(authRequest);
}

I am stuck here, both these things when I pass them are not at all null. Any help?


Answer:

Don't mock ArrayList instead create new array list and send it to actual class like below :

    final List<AuthenticationProvider> providers = new ArrayList<>();
    providers.add("AuthenticationProvider object values");

// when(providers.size()).thenReturn(1); -- no need of doing this stuff if you create direct arraylist object.

PS- If you are working with collections, do prefer creating new objects and send that object to actual class instead of mocking and sending, that is the best practice.

Hope it's useful.