We are trying to add unit testing to our Spring project Controllers
(BTW, the Integration tests work fine), but we are suffering a very odd behavior, When we add the Configuration with @EnableGlobalMethodSecurity
(with JSR-250 annotations) if the Controller implements an interface (whatever interface) the Controller
is not included by Spring application context as "request handler" (I checked it on method: org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.processCandidateBean(String beanName)
), that is, the requests mappings defined in the Controler (@PostMapping
, ...) are not registered as potential locations, but if the interface is removed, then the Controller and the path is found without problem.
This is my Controller (simplified) with a simple interface MyInterface
:
@RestController
@RequestMapping(path = "/api/customer")
@RolesAllowed({Role.Const.ADMIN})
public class CustomerController implements MyInterface {
@Override // The only method in MyInterface
public void myMethod(Object param) throws QOException {
System.out.println("Hello");
}
@PostMapping(path = {"/", ""})
public Customer create(@RequestBody Customer data) throws QOException {
return customerService.create(data);
}
}
And this is the Test class (if I remove the @EnableGlobalMethodSecurity
config class all works fine):
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = CustomerController.class)
@Import({ CustomerController.class, TestAuthConfiguration.class, TestAuthConfiguration2.class}) //, TestAuthConfiguration.class })
@ActiveProfiles({"test", "unittest"})
@WithMockUser(username = "test", authorities = { Role.Const.ADMIN })
class CustomerControllerTest {
private static final Logger LOG = LogManager.getLogger(CustomerControllerTest.class);
@EnableWebSecurity
protected static class TestAuthConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
LOG.info("configure(HttpSecurity http) ");
http.csrf().disable().authorizeRequests().filterSecurityInterceptorOncePerRequest(true)
.antMatchers("/api/session/**").permitAll() //
.antMatchers("/api/**").authenticated() //
.anyRequest().permitAll().and() //
.addFilterBefore(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
LOG.info("User authenticated: {}, roles: {}", auth.getName(), auth.getAuthorities());
}
filterChain.doFilter(request, response);
}
}, BasicAuthenticationFilter.class).sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
@EnableGlobalMethodSecurity(jsr250Enabled = true, securedEnabled = false)
protected static class TestAuthConfiguration2 extends GlobalMethodSecurityConfiguration {
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
}
@Autowired
MockMvc mockMvc;
@Test
void testCreate() throws Exception {
Customer bean = new Customer();
bean.setName("Test company");
when(customerServiceMock.create(bean)).thenReturn(bean);
mockMvc.perform(post("/api/customer")
.content(JsonUtils.toJSON(bean)).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.jsonPath("$.name").value("Test company"));
}
}
I don't understand what is happeing, I've tried to find an example of unit testing in controllers with security based on anotations JSR-250 (@RollesAllowed), but I didn't find anything useful, anyway this problem sounds (to me) to a bug, but I'm not sure, so any help is welcome.
The libraries versions:
- Spring Boot version: 2.2.2
- Spring Core: 5.2.1
- Mockito Core: 3.1.0
Try setting
proxyTargetClass
attribute totrue
. I was facing a similar problem and solved it by adding this.Also i would recommend reading javadocs for
EnableGlobalMethodSecurity
especially paying attention toproxyTargetClass
andmode
attributes.