Spring+SpirngMVC+Shiro+Junit4单元测试Controller方法

释放双眼,带上耳机,听听看~!

版本:Spring 4.1.8 Spring MVC 4.1.8,Shiro 1.2.4,Junit 4.12

网上关于对Controller的测试其实挺多的,不过也挺杂乱的,遇到各种坑。

  • 首先用到的是MockMvc这个测试框架,这个没什么好说的;
  • 其次测试需要用户登录的session问题,因为在controller中需要验证用户的信息
  • controller方法中各个参数属性注解的处理,如@ModelAttribute,@RequestBody

先上一个我自己项目的单元测试框架

单元测试基类


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1/**
2 * spring 测试基类 使用junit4进行单元测试
3 *
4 * @author junehappylove
5 *
6 */
7@RunWith(SpringJUnit4ClassRunner.class)
8@WebAppConfiguration
9// 加载配置文件,可以指定多个配置文件,locations指定的是一个数组
10@ContextConfiguration(locations = { "classpath:app.xml", "classpath:shiro-config-nosession.xml", "classpath:quartz-task.xml", "classpath:spring-mvc.xml" })
11// 启动事务控制
12@Transactional
13// 配置事务管理器,同时指定自动回滚
14@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)
15public class BaseJunit4Test extends BaseController {
16
17}
18
19

定义一个单元测试的基类,以后其他所有测试类都要继承BaseJunit4Test

业务测试

一个简单测试实例如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1public class WarningControllerTest extends BaseJunit4Test {
2    @Autowired
3    private WarningController warningController;
4    private MockMvc mockMvc;
5    @Before
6    public void setUp() throws Exception {
7        mockMvc = MockMvcBuilders.standaloneSetup(warningController).build();
8    }
9    @After
10    public void tearDown() throws Exception {
11    }
12    // 。。。。后面是具体的测试方法定义,这里省略
13}
14

上面对应解决一般的测试问题,就已经足够了
但是涉及到验证用户信息的情况下,上面代码显然不足,运行测试会发现SessionContext must be an HTTP compatible implementation.说白了就是没有session信息,那么我没就要添加Mock的session信息

解决用户session问题

首先需要引入属性MockHttpSession属性,其次还必须有用户登录的Controller


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1public class WarningControllerTest extends BaseJunit4Test {
2    @Autowired
3    private WarningController warningController;
4    @Autowired
5    private UserLogin userLogin;
6    private MockMvc mockMvc;
7    private MockMvc mockMvc2;
8    private MockHttpSession session;
9    @Before
10    public void setUp() throws Exception {
11        mockMvc = MockMvcBuilders.standaloneSetup(warningController).build();
12        mockMvc2 = MockMvcBuilders.standaloneSetup(userLogin).build();
13        this.session = doLogin();
14    }
15    @After
16    public void tearDown() throws Exception {
17    }
18    // 。。。。后面是具体的测试方法定义,这里省略
19
20    /**
21     * 获取用户登录的Session
22     * @return MockHttpSession
23     * @throws Exception 异常
24     */
25    private MockHttpSession doLogin() throws Exception {
26        ResultActions resultActions = this.mockMvc2.perform(MockMvcRequestBuilders.post("/userLogin/dologinDomain")
27                .param("username", "username").param("password", "password").param("vcode", ""));
28        resultActions.andExpect(MockMvcResultMatchers.status().isOk());
29        MvcResult result = resultActions.andReturn();
30        session = (MockHttpSession) result.getRequest().getSession();
31        return session;
32    }
33}
34

上面代码中说明在做用户验证测试的时候,必须首先模拟一遍用户登录,获取到session再做后面的测试,doLogin方法只是一个测试,里面提交的具体参数需要根据实际情况post。然后继续运行后续的测试代码,还是会发现SessionContext must be an HTTP compatible implementation.异常,这个异常可以跟踪源码查找,不过这里早就有大神做了这一步了,具体可以参考Junit+Spring MockMvc+Shiro时出现SessionContext和SecurityManager的错误解决方式
这位老兄说的很明白了,就是需要手动添加shiroFilter过滤器,那么这里就把


1
2
3
1mockMvc = MockMvcBuilders.standaloneSetup(warningController).build();
2mockMvc2 = MockMvcBuilders.standaloneSetup(userLogin).build();
3

修改如下:


1
2
3
1mockMvc = MockMvcBuilders.standaloneSetup(warningController).addFilters((Filter) SpringUtils.getBean("shiroFilter")).build();
2mockMvc2 = MockMvcBuilders.standaloneSetup(userLogin).addFilters((Filter) SpringUtils.getBean("shiroFilter")).build();
3

然后再测试就会发下正常了!

不过到了具体Controller方法又有问题,如下对@ModelAttribute @RequestBody的处理

Junit测试Controller(MockMVC使用),传输@ModelAttribute参数解决办法

测试方法


1
2
3
4
5
6
7
8
9
10
1@Test
2public void testSearch() throws Exception {
3    WarningModel model = new WarningModel();
4    ResultActions resultActions = this.mockMvc
5            .perform(MockMvcRequestBuilders.post("/warning/search").contentType(MediaType.APPLICATION_JSON)
6                    .param("start", "1").param("limit", "2").session(session).flashAttr("model", model));
7    resultActions.andExpect(MockMvcResultMatchers.status().isOk());
8    resultActions.andDo(MockMvcResultHandlers.print()).andReturn();
9}
10

上面方法测试一个查询操作,其中有参数 start,limit,这两个参数用户控制分页,还有参数model,这个对象里面承载的是查询的具体条件,使用flashAttr方法,然后定义了一个“model”的名称,那么在业务Controller的查询条件中必须显式的指名@ModelAttribute的名称是”model”,否则单元测试不通过!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1@RequestMapping("/search")
2@ResponseBody
3public ModelMap search(
4        @RequestParam(value = Constants.DEFAULT_CURRPAGE_MODEL_KEY, required = false, defaultValue = Constants.DEFAULT_PAGE_START) Integer start,
5        @RequestParam(value = Constants.DEFAULT_PAGE_SIZE_MODEL_KEY, required = false, defaultValue = Constants.DEFAULT_PAGE_SIZE) Integer limit,
6        @ModelAttribute(value = "model") WarningModel model) {//必须指定value属性名“model”
7    ModelMap modelMap = new ModelMap();
8    if (user() != null) {//验证登录用户合法性
9        PageHelper.startPage(start, limit);
10        List<WarningModel> list = warningService.query(model);
11        modelMap.addAttribute(Constants.DEFAULT_RECORD_MODEL_KEY, list);
12        modelMap.addAttribute(Constants.DEFAULT_COUNT_MODEL_KEY, ((Page<WarningModel>) list).getTotal());
13        modelMap.addAttribute(Constants.DEFAULT_SUCCESS_KEY, Boolean.TRUE);
14    } else {
15        modelMap.addAttribute(Constants.DEFAULT_SUCCESS_KEY, Boolean.FALSE);
16    }
17    return modelMap;
18}
19

Junit测试Controller(MockMVC使用),传输@RequestBody数据解决办法

关于参数是@RequestBody修饰,表明前台传来的是json串,测试模拟提交类型必须是application/json,并且使用content方法提交json内容,如下:


1
2
3
4
5
6
7
8
9
10
1@Test
2@Rollback
3public void testDeleteExist() throws Exception {
4    String content = "[{\"appid\":1}]";
5    this.mockMvc
6            .perform(MockMvcRequestBuilders.post("/warning/delete").session(session)
7                    .contentType(MediaType.APPLICATION_JSON).content(content))
8            .andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();
9}
10

好了,以上就是总结的使用Spring+SpirngMVC+Shiro+Junit4单元测试Controller方法以及注意事项

给TA打赏
共{{data.count}}人
人已打赏
安全技术

Bootstrap框架之排版

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索