0

I need to run a JUnit vs Spring MVC test case in which preconditions include that certain data is present in the HTTP Session. Most important is that I cannot wire a session-scoped bean: I must access httpServletContext.getSession().

Before showing code, let me explain. The controller I need to test assumes that a certain datum is stored in session, otherwise throws an exception. And that is the correct behaviour for now, because that controller is never invoked without a session and the session is always initialized with application data at login time. And obviously the controller is under security.

In my test, I just need to test whether this controller returns either a redirection or a 404 not found according to the request parameters.

I thought building my test case such as

@Autowired
private HttpServletRequest httpServletRequest;

@Autowired
private ModuleManager moduleManager;

@Autowired
private WebApplicationContext webApplicationContext;

private MenuItem rootMenu;

private MockMvc mockMvc;


@Before
public void setUp() throws Exception
{

    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
                             // No asserzioni
                             .build();

    rootMenu = moduleManager.getRootMenu()
                            .clone();
    httpServletRequest.getSession()
                      .setAttribute(MenuItem.SESSION_KEY, rootMenu);

    assertNotNull(rootMenu.getDescendant(existingSelectedMenu));
    assertNull(rootMenu.getDescendant(notExistingMenu));

}

@Test
public void testNavigate() throws Exception
{

    mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu))
           .andExpect(status().is3xxRedirection());

    assertNotSelected(rootMenu, existingSelectedMenu);

    mockMvc.perform(get("/common/navigate?target=" + notExistingMenu))
           .andExpect(status().is4xxClientError());

}

Part of the code is truly self-explaining. Anyway I expect /common/navigate to use the value I stored in the session. Like this

@RequestMapping(value = "/common/navigate",
        method = RequestMethod.GET)
public String navigate(@RequestParam("target") String target) throws NotFoundException
{

    MenuItem rootMenu = (MenuItem) httpServletRequest.getSession()
                                               .getAttribute(MenuItem.SESSION_KEY);
    if (rootMenu == null)
        throw new RuntimeException("Menu not found in session"); //Never happens

    MenuItem menuItem = rootMenu.getAndSelect(target);
    if (menuItem == null)
        throw new NotFoundException(MenuItem.class, target); //Expected

    return "redirect:" + menuItem.getUrl();
}

Now guess. What happens when I run my code?

RuntimeException is thrown in the line I commented as the menu object is not found in the session

Obviously the question is implicit now, but I will still write it: how do I inject data into the Session object so that controllers under test will have them available as precondition?

usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305

1 Answers1

0

Found the solution by myself now.

The problem is that the session itself must be mocked too. Spring provides a MockHttpSession class that does the trick. It can be pre-populated with all pre-conditions, but must be passed to every MockMvc request so that the mock will wire the session to the (mocked) servlet context.

Following code initializes the session

    mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext());

    mockHttpSession.setAttribute(MenuItem.SESSION_KEY, rootMenu);

Following performs the request with mocked session wired to it

mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu).session(mockHttpSession))
           .andExpect(status().is3xxRedirection());
usr-local-ΕΨΗΕΛΩΝ
  • 26,101
  • 30
  • 154
  • 305