title: Integration test for authentication filter with Spring Boot
body: <p>I would like to implement an integration test to test my authentication filter, implemented with Spring Security, with Spring Boot. But... I am lost...</p>
  
  <p>First, here is my "production" implementation:</p>
  
  <p>I have my web configurer adapter creating an authentication manager and declaring my filter:</p>
  
  <pre><code>@EnableWebSecurity
  public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
      @Autowired
      private IdentityService loginService;
      @Autowired
      private PersonService personService;
  
      @Override
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.userDetailsService(loginService).passwordEncoder(new BCryptPasswordEncoder());
      }
  
      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests().antMatchers(PATH_LOGIN).permitAll();
          http.authorizeRequests().antMatchers("/**").fullyAuthenticated();
  
          http.addFilterBefore(new AuthenticationFilter(PATH_LOGIN, authenticationManager(), personService),
              UsernamePasswordAuthenticationFilter.class);
      }
  </code></pre>
  
  <p>Then, here is my filter implementation:</p>
  
  <pre><code>public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {
  
      private ObjectMapper objectMapper = new ObjectMapper();
  
      private PersonService personService;
  
      protected AuthenticationFilter(String loginPath, AuthenticationManager authenticationManager,
          PersonService personService) {
          super(loginPath);
          this.personService = personService;
          setAuthenticationManager(authenticationManager);
      }
  
      @Override
      public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
          throws AuthenticationException, IOException, ServletException {
  
          LoginInfo loginInfo = objectMapper.readValue(request.getInputStream(), LoginInfo.class);
          UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
              loginInfo.getUsername(), loginInfo.getPassword());
  
          Authentication authentication = getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken);
          SecurityContextHolder.getContext().setAuthentication(authentication);
  
          return authentication;
  
      }
  
      @Override
      protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
          Authentication authResult) throws IOException, ServletException {
          Identity identity = (Identity) authResult.getPrincipal();
  
          Person person = personService.getPersonByMail(identity.getUsername());
  
          UserInfo userInfos = new UserInfo();
          userInfos.setUser(person);
          userInfos.setRoles(identity.getRoles());
  
          objectMapper.writeValue(response.getWriter(), userInfos);
      }
  }
  </code></pre>
  
  <p>Now, I have implemented the two services (PersonService &amp; IdentityService) which should be used as mock to prevent any database access:</p>
  
  <pre><code>@Profile("test")
  @Service
  public class PersonServiceMock implements PersonService {
  
      private static final Map&lt;String, Person&gt; USER_DB;
  
      static {
          Person valerian = new Student();
          valerian.setMail("valerian@savetheuniverse.com");
  
          USER_DB = new HashMap&lt;&gt;();
          USER_DB.put(valerian.getMail(), valerian);
      }
  
      @Override
      public Person getPersonByMail(String mail) {
          return USER_DB.get(mail);
      }
  
  }
  </code></pre>
  
  <p>-</p>
  
  <pre><code>@Profile("test")
  @Service
  public class IdentityServiceMock implements IdentityService {
  
      private static final Map&lt;String, Identity&gt; USER_DB;
  
      static {
          Identity valerian = new Identity("valerian@savetheuniverse.com");
  
          USER_DB = new HashMap&lt;&gt;();
          USER_DB.put(valerian.getUsername(), valerian);
  
          BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
  
          USER_DB.forEach((key, value) -&gt; {
              value.setEnabled(true);
              value.setLocked(false);
              value.setPassword(encoder.encode("pa$$w0rd"));
          });
      }
  
      @Override
      public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          UserDetails ud = USER_DB.get(username);
          return ud;
      }
  }
  </code></pre>
  
  <p>In the end, here is my "start of test" I wrote but that does not work because it seems it wants to retrieve the "production" implementation of the service instead of my fake one:</p>
  
  <pre><code>@ActiveProfiles("test")
  @RunWith(SpringRunner.class)
  @SpringBootTest
  @WebAppConfiguration
  public class AuthenticationTests {
  
      @Autowired
      private Filter filterChainProxy;
  
      @Autowired
      private WebApplicationContext context;
  
      private MockMvc mockMvc;
  
      @Before
      public void before() {
          mockMvc = MockMvcBuilders.webAppContextSetup(context).addFilters(filterChainProxy).build();
  }
  
      @Test
      public void login() throws Exception {
  
          ObjectMapper objectMapper = new ObjectMapper();
          LoginInfo loginInfo = new LoginInfo("valerian@savetheworld.com", "pa$$w0rd");
  
          MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/login")
              .content(objectMapper.writeValueAsString(loginInfo));
  
          Person person = new Student("valerian", "none", "valerian@savetheworld.com");
          UserInfo expectedUserInfo = new UserInfo(person, null);
  
          String expectedJSonContent = objectMapper.writeValueAsString(expectedUserInfo);
  
          mockMvc.perform(requestBuilder).andExpect(MockMvcResultMatchers.status().isOk())
              .andExpect(MockMvcResultMatchers.content().json(expectedJSonContent));
  
      }
  
  }
  </code></pre>
  
  <p>Did I misunderstood something? Can you help me, please?</p>