I'm trying to write an integration test that hits a REST endpoint and gets data for a specific authenticated user (the one I'm setting up in the test). I initially tried my setup with mockMvc = webAppContextSetup(wac).apply(springSecurity()).build()
, but that was consistently failing with a BeanInstantiationException
. With the help of Testing Spring Security and MvcMock using a custom UserDetails implementation, I was able to overcome that issue by switching up my setUp. Now that I've switched my setup to use standaloneSetup
, I'm able to call into my controller. But, no matter what I do, I'm unable to get the custom UserDetails object that I'm creating in my test into the method in my controller that the call to MockMvc ends up at.
I'm using Spring 4.2.2 and Spring Security 4.0.1.
My test code looks something like this:
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { RestControllerITConfig.class })
@WebAppConfiguration
public class ControllerIT {
private MockMvc mockMvc;
@Before
public void setUp() {
mockMvc = standaloneSetup(new Controller())
.setCustomArgumentResolvers(new AuthenticationPrincipalArgumentResolver())
.build();
}
@Test
public void testGetEndpoint() throws Exception {
CustomUserDetails userDetails = new CustomUserDetails("John","Smith", "abc123");
assertNotNull(userDetails);
MvcResult result = mockMvc.perform(
get("/somepath").with(user(userDetails)))
.andExpect(status().isOk())
.andExpect(content().contentType("text/json")));
}
}
@Configuration
@ComponentScan(basePackageClasses = { AuthenticationConfig.class })
public class RestControllerITConfig {
}
@Configuration
@EnableWebSecurity
@ComponentScan("com.my.authentication.package")
public class AuthenticationConfig extends WebSecurityConfigurerAdapter {
// snip
}
@Target({ ElementType.PARAMETER, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@AuthenticationPrincipal
public @interface MyUser {
}
@RestController
@RequestMapping("/somepath")
public class Controller {
@RequestMapping(value = "", method = RequestMethod.GET)
public ResponseEntity<Object> getSomePath(@MyUser CustomUserDetails customUser) {
customUser.getName(); // <== Causes NPE
}
}
There are other pieces of irrelevant configuration that have been omitted for the sake of brevity. I really don't understand why the custom UserDetails object that I'm explicit creating in the test isn't getting passed into my REST controller, but rather a null object is. What am I doing wrong here? Does anyone have a working example of a similar case? Many thanks in advance.
I was finally able to get this to work by explicitly adding my AuthenticationConfig's filter onto the
StandaloneMockMvcBuilder
. So my setup now looks like: