Reputation: 125
I'm testing my Spring MVC controllers using JUnit, Mockito & spring-test. Unfortunately, the tests are ignoring the @PreAuthorize annotations on my controller methods, and I've been unable to resolve this. Key code snippets are below, though I've removed irrelevant logic for mocking responses from MyController's dependencies to keep it short. I'm using Spring 3.2 & JUnit 4, and I'm running the tests both via Maven and directly through Eclipse (Run as -> JUnit test).
Right now I am expecting the getAccounts_ReturnsOkStatus test to fail, as I have not provided any auth and the method that the /accounts route maps onto is annotated with a pre-auth annotation, however the method is being invoked and the pre-auth check bypassed.
MyControllerTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
public class MyControllerTest {
private MockMvc mockMvc;
@Mock
private MyService myService;
@InjectMocks
private MyController myController;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(myController).build();
}
@Test
public void getAccounts_ReturnsOkStatus() throws Exception {
// Make the GET request, and verify that it returns HttpStatus.OK (200)
mockMvc.perform(MockMvcRequestBuilders.get("/accounts"))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
applicationContext-test.xml
<sec:global-method-security pre-post-annotations="enabled" />
<bean id="applicationContextProvider" class="com.myapp.springsupport.ApplicationContextProvider" />
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider>
<sec:user-service>
<sec:user name="test-superuser" password="test-pwd" authorities="ROLE_SUPERUSER" />
</sec:user-service>
</sec:authentication-provider>
</sec:authentication-manager>
MyController.java
@PreAuthorize("isAuthenticated() and hasRole('ROLE_SUPERUSER')")
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
@ResponseBody
public Collection<Account> getAccounts() {
return new ArrayList<Account>();
}
The applicationContext-test app context is definitely being used, since manually authenticating using
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(am.authenticate(auth));
only works with the user credentials specified in the test config (in the absence of any other authentication-provider). Additionally, I can be sure that the pre-auth is being ignored, since I've tested using SEL to invoke a method & debugged.
What am I missing?
Upvotes: 5
Views: 10662
Reputation: 1753
MockMvcBuilders.standaloneSetup
gets a MyController
instantiated manually ( without Spring and therefore without AOP). Therefore the @PreAuthorize
is not intercepted and security check is skipped. You can therefore @Autowire your controller and pass it to MockMvcBuilders.standaloneSetup
to Mock MyService
use @MockBean
so that every instance of the service gets replaced with the Mock.
Upvotes: -1
Reputation: 2080
You have to enable spring security for testing when building mockMvc
.
In Spring 4 it's like this:
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
In Spring 3 it's like this:
@Autowired
private Filter springSecurityFilterChain;
...
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.build();
For more details see:
Upvotes: 2