Bardo
Bardo

Reputation: 73

How to test a Spring Controller that is secured

I currently have an app built with Spring Boot 2, Spring MVC, Spring Data/JPA and Thymeleaf.

I'm writing some unit/integration tests and I'd like to test the controller, which is secured by SpringSecurity backed by a database with registered users.

What would be the best approach here to test it? I've unsuccessfully tried a few of them like using annotations like @WithMockUser.

Edit: Just a reminder that I'm not testing @RestControllers. I'm directly injecting a @Controller on my test class and calling its methods. It works just fine without Spring Security.

One example:

@Controller
public class SecuredController {
  @GetMapping("/")
  public String index() {
    return "index";
  }
}

The / path is secured by Spring Security and would normally redirect to /login to authenticate the user.

My unit test would look like this:

@WebMvcTest(controllers = SecuredController.class)
class SecuredControllerTest {

  @Autowired
  private SecuredController controller;

  @Autowired
  private MockMvc mockMvc;

  @Test
  @WithMockUser(username = "user", password = "pass", roles = {"USER"})
  public void testAuthenticatedIndex() throws Exception {
    mockMvc.perform(get("/"))
        .andExpect(status().isOk())
        .andDo(print());
  }
}

The first errors I get is that is asks me to inject my UserDetailsService implementation, which is something that I'd like to avoid. But if I do inject the service, the test works, but returns 404 instead of 200.

Any ideas?

Upvotes: 1

Views: 2123

Answers (2)

Ahmed Sayed
Ahmed Sayed

Reputation: 1564

You will need to add your security configurations to the Spring context by importing your WebSecurityConfigurerAdapter class.

@WebMvcTest(controllers = SecuredController.class)
@Import(SecuredControllerTest.Config.class)
class SecuredControllerTest {

   @Configuration
   @EnableWebSecurity
   static class Config extends MyWebSecurityConfigurerAdapter {
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
          auth.inMemoryAuthentication().withUser("user").password("pa$$").roles("USER");
          auth.inMemoryAuthentication().withUser("admin").password("pa$$").roles("ADMIN");
      }
   }

   ...
}

The embedded static class Config is just to change where we get the users from, in this case an inMemoryAuthentication will be enough.

Upvotes: 1

Vy Do
Vy Do

Reputation: 52746

In test class, use annotations

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)

in setup test method

@Before

In real test method

@WithMockUser("spring")
@Test

Testing Spring Security like these examples

https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security

https://www.baeldung.com/spring-security-integration-tests

Upvotes: 0

Related Questions