1

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?

Bardo
  • 73
  • 4
  • I am not sure if it will help but you can have a look at https://stackoverflow.com/a/43458626/2629411 – sandiee Jan 23 '20 at 09:53
  • Not really, that answer helps to test `@RestController`s and I'm testing `@Controller`s directly, not calling them through a `RestTemplate`, but directly injecting them on the test class and calling their methods. – Bardo Jan 23 '20 at 10:03
  • can you share some code snippet? – sandiee Jan 23 '20 at 10:05
  • @sandiee I've added some snippets. Hope it helps. – Bardo Jan 23 '20 at 10:20

2 Answers2

1

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.

Ahmed Sayed
  • 1,429
  • 12
  • 12
  • I'm configuring with a `WebSecurityConfigurerAdapter`. But it loads the user from a database, and I'd like to mock that somehow. Should I create a new implementation of `WebSecurityConfigurerAdapter` just for testing? – Bardo Jan 23 '20 at 12:06
0

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

Vy Do
  • 46,709
  • 59
  • 215
  • 313