4

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?

René Scheibe
  • 1,970
  • 2
  • 14
  • 20
DGoodman
  • 115
  • 1
  • 8

2 Answers2

2

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:

Community
  • 1
  • 1
René Scheibe
  • 1,970
  • 2
  • 14
  • 20
  • Thanks for that. The main problem I'm having now is that using 'webAppContextSetup' rather than 'standaloneSetup' requires that I include my controller in the applicationContext using component-scan. However doing this requires that I also include all the classes autowired into my controller, even though they're all being mocked in the test and the implementations themselves are not being used. There's clearly something wrong with my setup here, but I can't work out what. – DGoodman Feb 16 '17 at 10:58
  • 1
    Have a look at http://docs.spring.io/spring-security/site/docs/current/reference/html/test-mockmvc.html#test-mockmvc-securitycontextholder-rpp. It explains how you can still use `standaloneSetup`. When using `webAppContextSetup` have a look at `@MockBean` (http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html) that can be used to mock autowired beans in your controller. – René Scheibe Feb 16 '17 at 16:54
  • @RenéScheibe how about annotations on service layer? – VB_ Apr 24 '18 at 14:14
-1

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.

user1928596
  • 1,503
  • 16
  • 21