t0mppa t0mppa - 18 days ago 8
Java Question

Add user role to request in Spring MVC Test Framework

Started studying Spring Test MVC Framework at the office today, it looks handy, but facing some serious trouble right off the bat. Spent a few hours googling, but couldn't find anything related to my issue.

Here's my very simple test class:

import static org.hamcrest.Matchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = WebAppContext.class)
public class ControllerTests {

@Autowired
private WebApplicationContext wac;

private MockMvc mockMvc;

@Before
public void setup() {
mockMvc = webAppContextSetup(wac).build();
}

@Test
public void processFetchErrands() throws Exception {
mockMvc.perform(post("/errands.do?fetchErrands=true"))
.andExpect(status().isOk())
.andExpect(model().attribute("errandsModel", allOf(
hasProperty("errandsFetched", is(true)),
hasProperty("showReminder", is(false)))));
}
}


The test reaches the following controller, but fails on first
if
clause thanks to not being authorized properly.

@RequestMapping(method = RequestMethod.POST, params="fetchErrands")
public String processHaeAsioinnit(HttpSession session, HttpServletRequest request, ModelMap modelMap,
@ModelAttribute(ATTR_NAME_MODEL) @Valid ErrandsModel model,
BindingResult result, JopoContext ctx) {
if (request.isUserInRole(Authority.ERRANDS.getCode())) {
return Page.NO_AUTHORITY.getCode();
}

[...]
}


How do I add a user role for the
MockHttpServletRequest
that gets created by
MockMvcRequestBuilders.post()
, so that I can get past the authority check on my controller?

I know
MockHttpServletRequest
has a method
addUserRole(String role)
, but since
MockMvcRequestBuilders.post()
returns a
MockHttpServletRequestBuilder
, I never get my hands on the
MockHttpServletRequest
and thus cannot call that method.

Checking the Spring source,
MockHttpServletRequestBuilder
has no methods related to user roles, nor is the
MockHttpServletRequest.addUserRole(String role)
ever called in that class, so I have no idea how to tell it to add a user role into the request.

All I can think of is adding a custom filter to filter chain and calling a custom
HttpServletRequestWrapper
from there that provides an implementation of
isUserInRole()
, but that seems a little extreme for such a case. Surely the framework should offer something more practical?

Answer

Spring MVC Test has the principal() method that allows to mock the request credentials for cases like this. This is an example of a test where some mock credentials are set:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:spring/mvc-dispatcher-servlet.xml")
public class MobileGatewayControllerTest {

private MockMvc mockMvc;

@Autowired
private WebApplicationContext wac;  

@Autowired
private Principal principal;

@Autowired
private MockServletContext servletContext;

@Before 
public void init()  {
    mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}


@Test
public void testExampleRequest() throws Exception {

    servletContext.declareRoles("ROLE_1");

    mockMvc.perform(get("/testjunit")
    .accept(MediaType.APPLICATION_JSON)
    .principal(principal))
    .andDo(print())
    .andExpect(status().isOk())
    .andExpect(content().contentType("application/json"))
    .andExpect(jsonPath("$.[1]").value("test"));
}

}

and this is an example of how to create a mock principal:

@Configuration
public class SetupTestConfig {

@Bean
public Principal createMockPrincipal()  {
    Principal principal = Mockito.mock(Principal.class);
    Mockito.when(principal.getName()).thenReturn("admin");  
    return principal;
}

}

Comments