title: Spring Boot Unit Testing with Spring Security
body: <p>I have a simple application that I have setup with spring security using a custom MySql Database. Now I'm writing test cases for it and they seems to fail on login page and anything that works after the login. My question is how do I write test cases for it to check the successful login and the subsequent requests?</p>
  
  <p>My Security Config:</p>
  
  <pre><code>@Configuration
  @EnableWebSecurity
  public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
      @Autowired
      private BCryptPasswordEncoder bCryptPasswordEncoder;
  
      @Autowired
      private DataSource dataSource;
  
      @Value("${spring.queries.users-query}")
      private String usersQuery;
  
      @Value("${spring.queries.roles-query}")
      private String rolesQuery;
  
      @Autowired 
      private CustomAuthenticationSuccessHandler successHandler;
  
      /** Providing the queries and data source for security*/
      @Override
      protected void configure(AuthenticationManagerBuilder auth)
              throws Exception 
      {
          auth.
              jdbcAuthentication()
                  .usersByUsernameQuery(usersQuery)
                  .authoritiesByUsernameQuery(rolesQuery)
                  .dataSource(dataSource)
                  .passwordEncoder(bCryptPasswordEncoder);
      }
  
      /** Defining fine grained access for ADMIN and CUSTOMER user */
      @Override
      protected void configure(HttpSecurity http) throws Exception {
  
          http.
              authorizeRequests()
                  .antMatchers("/").permitAll()
                  .antMatchers("/login").permitAll()
                  .antMatchers("/registration").permitAll()
                  .antMatchers("/user/**").hasAuthority(AppRole.CUSTOMER.toString())
                  .antMatchers("/health/**").hasAuthority(AppRole.ADMIN.toString())
                  .antMatchers("/admin/**").hasAuthority(AppRole.ADMIN.toString()).anyRequest()
                  .authenticated().and().csrf().disable().formLogin()
                  .loginPage("/login").failureUrl("/login?error=true")
                  .successHandler(successHandler)
                  .usernameParameter("username")
                  .passwordParameter("password")
                  .and().logout()
                  .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                  .logoutSuccessUrl("/").and().exceptionHandling()
                  .accessDeniedPage("/access-denied");
      }
  
      /** Defining ant matchers that should ignore the paths and provide no access to any one */
      @Override
      public void configure(WebSecurity web) throws Exception 
      {
          web
             .ignoring()
             .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**");
      }
  
  }
  </code></pre>
  
  <p>My Custom Success Handler:</p>
  
  <pre><code>@Component
  @Configuration
  public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler 
  {
      /** Getting reference to UserService */
      @Autowired
      private UserService userService;
  
      @Override
      public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
              HttpServletResponse httpServletResponse, Authentication authentication) 
                      throws IOException, ServletException, RuntimeException 
      {
          HttpSession session = httpServletRequest.getSession();
          User authUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
          com.crossover.techtrial.java.se.model.User user = userService.findUserByUsername(authUser.getUsername());
          session.setAttribute("userId", user.getUserId());
          session.setAttribute("username", authUser.getUsername());
          session.setAttribute("accountId", user.getAccountId());
          //set our response to OK status
          httpServletResponse.setStatus(HttpServletResponse.SC_OK);
          Collection&lt;? extends GrantedAuthority&gt; authorities = authentication.getAuthorities();
          authorities.forEach(authority -&gt; 
                                  { 
                                      if(authority.getAuthority().equals(AppRole.ADMIN.toString())) 
                                      { 
                                          session.setAttribute("role", AppRole.ADMIN);
                                          try
                                          {
                                              //since we have created our custom success handler, its up to us to where
                                              //we will redirect the user after successfully login
                                              httpServletResponse.sendRedirect("/admin/home");
                                          } 
                                          catch (IOException e) 
                                          {
                                              throw new RuntimeException(e);
                                          }                                                                           
                                      }
                                      else if (authority.getAuthority().equals(AppRole.CUSTOMER.toString()))
                                      {
                                          session.setAttribute("role", AppRole.CUSTOMER);
                                          try
                                          {
                                              //since we have created our custom success handler, its up to us to where
                                              //we will redirect the user after successfully login
                                              httpServletResponse.sendRedirect("/user/home");
                                          } 
                                          catch (IOException e) 
                                          {
                                              throw new RuntimeException(e);
                                          }   
                                      }
                                  });
  
      }
  
  }
  </code></pre>
  
  <p>After some seraching I tried to write test cases like this but they don't seem to be working:</p>
  
  <pre><code>    @RunWith(SpringRunner.class)
      @SpringBootTest
      public class TrialApplicationTests 
      {
          @Autowired
          private WebApplicationContext webApplicationContext;
  
          @Autowired
          private FilterChainProxy springSecurityFilterChain;
  
          @Autowired
          private MockHttpServletRequest request;
  
          private MockMvc mockMvc;
  
  
          @Test
          public void contextLoads() 
          {
          }
  
          @Before
          public void setup() 
          {
              mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                      .addFilters(springSecurityFilterChain)
                      .build();
          }
          @Test
          public void verifiesLoginPageLoads() throws Exception 
          {
              mockMvc.perform(MockMvcRequestBuilders.get("/"))
                     .andExpect(MockMvcResultMatchers.model().hasNoErrors())
                     .andExpect(MockMvcResultMatchers.view().name("login"))
                     .andExpect(MockMvcResultMatchers.status().isOk());
          }
  
          @Test
          public void testUserLogin()  throws Exception
          {
              HttpSession session = mockMvc.perform(post("/login")
                      .contentType(MediaType.APPLICATION_FORM_URLENCODED) 
                      .param("username", "test")
                      .param("password", "test123")
                      )
                      .andExpect(MockMvcResultMatchers.status().isOk())
                      //.andExpect(redirectedUrl("/user/home"))
                      .andReturn()
                      .getRequest()
                      .getSession();
  
              request.setSession(session);
  
              SecurityContext securityContext = (SecurityContext)   session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
  
              SecurityContextHolder.setContext(securityContext);
          }
  
          @Test
          public void testRetrieveUserBookings() throws Exception
          {
              testUserLogin();
  
              mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
                  .andExpect(MockMvcResultMatchers.model().hasNoErrors())
                  .andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
                  .andExpect(MockMvcResultMatchers.view().name("user/bookings"))
                  .andExpect(content().string(containsString("Booking")));
          }
  
      }
  
  I searched on the net and there are links WithMockUser and UserDetails, but the problem is as you can see I'm setting a my primary key userId in the session in my custom success handler. So I would also need to get the session in my test. Please tell me the simplest way to write tests that will work, possibly with code since I'm new with security and all such. 
  
  UPDATE:
  
  I changed the code as suggested but still getting the 404 error on my testRetrieveUserBookings. Any more ideas?
  
  @RunWith(SpringRunner.class)
  @ContextConfiguration
  @SpringBootTest
  @FixMethodOrder(MethodSorters.NAME_ASCENDING)
  @TestExecutionListeners(listeners={ServletTestExecutionListener.class,
          DependencyInjectionTestExecutionListener.class,
          DirtiesContextTestExecutionListener.class,
          TransactionalTestExecutionListener.class,
          WithSecurityContextTestExecutionListener.class})
  public class TrialApplicationTests 
  {
      @Autowired
      private WebApplicationContext webApplicationContext;
  
      MockMvc mockMvc; 
  
      @Autowired
      ForestApiClient apiClient;
  
      @Autowired
      AccountClient accountClient;
  
      @Autowired
      AirlineClient airlineClient;
  
      @Autowired
      UserService userService;
  
      private final String INTEGRATION_ACCOUNT = "account1";
  
      private MockHttpSession mockSession;
  
      private Authentication authentication;
  
      @Test
      public void contextLoads() 
      {
      }
  
      @Before
      public void setup() 
      {
          mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                  //.addFilters(springSecurityFilterChain)
                  .build();   
  
          mockSession = new MockHttpSession(webApplicationContext.getServletContext(), UUID.randomUUID().toString()); 
          mockSession.setAttribute("userId", 3);
          mockSession.setAttribute("accountId", "ZWR26539");
      }
  
      @Test
      public void testVerifiesLoginPageLoads() throws Exception 
      {
          mockMvc.perform(MockMvcRequestBuilders.get("/"))
                 .andExpect(MockMvcResultMatchers.model().hasNoErrors())
                 .andExpect(MockMvcResultMatchers.view().name("login"))
                 .andExpect(MockMvcResultMatchers.status().isOk());
      }
  
      @Test
      public void testRegistration()  throws Exception
      {
          mockMvc.perform(post("/registration")
                  .contentType(MediaType.APPLICATION_FORM_URLENCODED) 
                  .param("username", "test2")
                  .param("password", "test123")
                  .param("email", "crossovertestuser@gmail.com")
                  .param("address", "Some Address")
                  .param("accountCurrency", "USD")
                  )
                  .andExpect(MockMvcResultMatchers.status().isOk())
                  .andExpect(MockMvcResultMatchers.model().hasNoErrors())
                  .andExpect(MockMvcResultMatchers.model().attributeExists("user"))
                  .andExpect(MockMvcResultMatchers.view().name("registration"))
                  .andExpect(content().string(containsString("User has been registered successfully")));
      }
  
      @Test
      @WithMockUser(username="test",roles={"USER","ADMIN"})
      public void testRetrieveUserBookings() throws Exception
      {
  
          mockMvc.perform(MockMvcRequestBuilders.get("user/bookings"))
              .andExpect(MockMvcResultMatchers.model().hasNoErrors())
              .andExpect(MockMvcResultMatchers.model().attributeExists("bookings"))
              .andExpect(MockMvcResultMatchers.view().name("user/bookings"))
              .andExpect(content().string(containsString("Booking")));
      }
  }
  </code></pre>