从 JUnit 测试中将数据注入会话
Inject data into session from JUnit test
我需要 运行 JUnit vs Spring MVC 测试用例,其中先决条件包括某些数据存在于 HTTP Session
中。最重要的是我无法连接 session
范围的 bean:我必须访问 httpServletContext.getSession()
.
在展示代码之前,让我解释一下。我需要测试的控制器假定某个数据存储在会话中,否则抛出异常。这是目前正确的行为,因为在没有会话的情况下永远不会调用该控制器,并且会话总是在登录时使用应用程序数据进行初始化。显然控制器处于安全状态。
在我的测试中,我只需要根据请求参数测试这个控制器returns是重定向还是404未找到。
我想构建我的测试用例,例如
@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());
}
部分代码真正做到了不言自明。无论如何,我希望 /common/navigate
使用我存储在会话中的值。像这样
@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();
}
现在猜猜。当我 运行 我的代码时会发生什么?
在我评论的行中抛出 RuntimeException,因为在会话中找不到菜单对象
显然这个问题现在是隐含的,但我仍然会写它:我如何将数据注入 Session 对象,以便被测控制器将它们作为前提条件可用?
现在自己找到了解决方案。
问题是会话本身也必须被模拟。 Spring 提供了一个 MockHttpSession
class 来解决问题。它可以预先填充所有先决条件, 但是 必须传递给每个 MockMvc
请求,以便模拟将会话连接到(模拟的)servlet 上下文。
以下代码初始化会话
mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext());
mockHttpSession.setAttribute(MenuItem.SESSION_KEY, rootMenu);
以下执行连接到它的模拟会话的请求
mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu).session(mockHttpSession))
.andExpect(status().is3xxRedirection());
我需要 运行 JUnit vs Spring MVC 测试用例,其中先决条件包括某些数据存在于 HTTP Session
中。最重要的是我无法连接 session
范围的 bean:我必须访问 httpServletContext.getSession()
.
在展示代码之前,让我解释一下。我需要测试的控制器假定某个数据存储在会话中,否则抛出异常。这是目前正确的行为,因为在没有会话的情况下永远不会调用该控制器,并且会话总是在登录时使用应用程序数据进行初始化。显然控制器处于安全状态。
在我的测试中,我只需要根据请求参数测试这个控制器returns是重定向还是404未找到。
我想构建我的测试用例,例如
@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());
}
部分代码真正做到了不言自明。无论如何,我希望 /common/navigate
使用我存储在会话中的值。像这样
@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();
}
现在猜猜。当我 运行 我的代码时会发生什么?
在我评论的行中抛出 RuntimeException,因为在会话中找不到菜单对象
显然这个问题现在是隐含的,但我仍然会写它:我如何将数据注入 Session 对象,以便被测控制器将它们作为前提条件可用?
现在自己找到了解决方案。
问题是会话本身也必须被模拟。 Spring 提供了一个 MockHttpSession
class 来解决问题。它可以预先填充所有先决条件, 但是 必须传递给每个 MockMvc
请求,以便模拟将会话连接到(模拟的)servlet 上下文。
以下代码初始化会话
mockHttpSession = new MockHttpSession(webApplicationContext.getServletContext());
mockHttpSession.setAttribute(MenuItem.SESSION_KEY, rootMenu);
以下执行连接到它的模拟会话的请求
mockMvc.perform(get("/common/navigate?target=" + existingSelectedMenu).session(mockHttpSession))
.andExpect(status().is3xxRedirection());