title: Using Spring security test to test a secured Spring MVC controller
body: <p>following the <a href="http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test" rel="nofollow">documentation</a>  about using <strong>Spring Security Test</strong> to  write tests for a spring MVC app that is wired behind <strong>Spring Security</strong>.</p>
  
  <p>This is a vanilla spring-boot application employing a typical spring-security wiring. Here's the main Application.java</p>
  
  <pre class="lang-java prettyprint-override"><code>@SpringBootApplication
  public class Application {
  
      private static final Logger log = LoggerFactory.getLogger(Application.class);
  
      public static void main(String[] args) {
          SpringApplication.run(Application.class);
      }
  }
  </code></pre>
  
  <p>Here's the wiring for the spring-security:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Configuration
  @EnableWebSecurity
  public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  
      @Autowired
      @Qualifier("customUserDetailsService")
      UserDetailsService userDetailsService;
  
      @Autowired
      public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(userDetailsService);
      }
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests()
                  .antMatchers("/","/sign_up").permitAll()
                  .anyRequest().authenticated()
                  .and()
              .formLogin()
                  .and()
              .csrf()
                  .and()
              .exceptionHandling()
                  .accessDeniedPage("/access_denied")
                  .and()
              .logout()
                  .permitAll();
      }
  }
  </code></pre>
  
  <p>As you may see, all requests require to be authenticated except for those for "/" and "/sign_up". I have verified by deploying the application that the authentication schemes work just fine.</p>
  
  <hr>
  
  <p>Now comes the interesting part: writing spring mvc tests. The <a href="http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test" rel="nofollow">link</a> I provided gives some nice ways to write such tests where the <em>spring-security-test</em> framework allows inserting mock users/security-contexts. I've taken the approach of</p>
  
  <ol>
  <li>defining a mock UserDetails interface, and</li>
  <li>using a SecurityContextFactory to create a SecurityContext, which</li>
  <li>should be inserted into the application context during test launch.</li>
  </ol>
  
  <p>The code for 1. is as follows:</p>
  
  <pre class="lang-java prettyprint-override"><code>@WithSecurityContext(factory=WithMockUserDetailsSecurityContextFactory.class)
  public @interface WithMockUserDetails {
      String firstName() default "apil";
      String lastName() default "tamang";
      String password() default "test";
      long id() default 999;
      String email() default "apil@test.com";
  
  
  }
  </code></pre>
  
  <p>The code for 2. is as follows:</p>
  
  <pre class="lang-java prettyprint-override"><code>final class WithMockUserDetailsSecurityContextFactory
      implements WithSecurityContextFactory&lt;WithMockUserDetails&gt;{
  
  
      @Override
      public SecurityContext createSecurityContext(WithMockUserDetails mockUserDetails) {
  
  
          /*
           * Use an anonymous implementation for 'UserDetails' to return a
           * mock authentication object, which is then set to the SecurityContext
           * for the test runs.
           */
          UserDetails principal=new UserDetails() {
              @Override
              public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
  
                  //another anonmyous interface implementation.
                  GrantedAuthority auth=new GrantedAuthority() {
                      @Override
                      public String getAuthority() {
                          return "ROLE_USER";
                      }
                  };
                  List&lt;GrantedAuthority&gt; authorities=new ArrayList&lt;&gt;();
                  authorities.add(auth);
                  return authorities;
              }
  
              @Override
              public String getPassword() {
                  return mockUserDetails.password();
              }
  
              @Override
              public String getUsername() {
                  return mockUserDetails.email();
              }
  
              @Override
              public boolean isAccountNonExpired() {
                  return true;
              }
  
              @Override
              public boolean isAccountNonLocked() {
                  return true;
              }
  
              @Override
              public boolean isCredentialsNonExpired() {
                  return true;
              }
  
              @Override
              public boolean isEnabled() {
                  return true;
              }
          };
          Authentication authentication=new
                  UsernamePasswordAuthenticationToken(principal,principal.getPassword(),principal.getAuthorities());
          SecurityContext context= SecurityContextHolder.createEmptyContext();
          context.setAuthentication(authentication);
          return context;
      }
  }
  </code></pre>
  
  <hr>
  
  <p>Finally, here's the test class:</p>
  
  <pre class="lang-java prettyprint-override"><code>@RunWith(SpringJUnit4ClassRunner.class)
  @SpringApplicationConfiguration(classes = {Application.class})
  @WebAppConfiguration
  public class UserControllerTest {
  
      @Autowired
      private WebApplicationContext context;
  
      @Autowired
      private Filter springSecurityFilterChain;
  
      private MockMvc mvc;
  
      @Before
      public void setup(){
          mvc= MockMvcBuilders
                  .webAppContextSetup(context)                    
                  .apply(springSecurity())
                  .build();
      }
  
      @Test
      public void testRootIsOk() throws Exception {
          mvc.perform(get("/"))
                  .andExpect(status().isOk());
      }
  
      @Test
      public void expectRedirectToLogin() throws Exception {
          mvc.perform(get("/testConnect"))
                  .andExpect(redirectedUrl("http://localhost/login"));
  
      }
  
      @Test
      @WithMockUserDetails
      public void testAuthenticationOkay() throws Exception {
          mvc.perform(get("/testConnect"))
                  .andExpect(content().string("good request."));
      }
  
  }
  </code></pre>
  
  <p>Output of Test Run:</p>
  
  <ol>
  <li>Test 1 passes.</li>
  <li>Test 2 passes.</li>
  <li>Test 3 fails. Expected output  but got &lt;>. </li>
  </ol>
  
  <p>Very likely Test 3 failed because of the 'SecurityContext' never got appropriately populated. As per the documentation, it should have worked. Not sure what I missed. Any help is much appreciated.</p>