如何用SpringBoot 进行测试
普通测试
假设要测试一个工具类StringUtil(com.rxliuli.example.springboottest.util.StringUtil)
/** *用于测试的字符串工具类 * *@authorrxliuli */ publicclassStringUtil{ /** *判断是否为空 * *@paramstring要进行判断的字符串 *@return是否为null或者空字符串 */ publicstaticbooleanisEmpty(Stringstring){ returnstring==null||string.isEmpty(); } /** *判断是否为空 * *@paramstring要进行判断的字符串 *@return是否为null或者空字符串 */ publicstaticbooleanisNotEmpty(Stringstring){ return!isEmpty(string); } /** *判断是否有字符串为空 * *@paramstrings要进行判断的一个或多个字符串 *@return是否有null或者空字符串 */ publicstaticbooleanisAnyEmpty(String...strings){ returnArrays.stream(strings) .anyMatch(StringUtil::isEmpty); } /** *判断字符串是否全部为空 * *@paramstrings要进行判断的一个或多个字符串 *@return是否全部为null或者空字符串 */ publicstaticbooleanisAllEmpty(String...strings){ returnArrays.stream(strings) .allMatch(StringUtil::isEmpty); } }
需要添加依赖spring-boot-starter-test以及指定assertj-core的最新版本
org.springframework.boot spring-boot-starter-test test org.assertj assertj-core 3.9.1 test
这里指定assertj-core的版本是为了使用较新的一部分断言功能(例如属性lambda断言)
/** *@authorrxliuli */ publicclassStringUtilTest{ privateStringstrNull=null; privateStringstrEmpty=""; privateStringstrSome="str"; @Test publicvoidisEmpty(){ //测试null assertThat(StringUtil.isEmpty(strNull)) .isTrue(); //测试empty assertThat(StringUtil.isEmpty(strEmpty)) .isTrue(); //测试some assertThat(StringUtil.isEmpty(strSome)) .isFalse(); } @Test publicvoidisNotEmpty(){ //测试null assertThat(StringUtil.isNotEmpty(strNull)) .isFalse(); //测试empty assertThat(StringUtil.isNotEmpty(strEmpty)) .isFalse(); //测试some assertThat(StringUtil.isNotEmpty(strSome)) .isTrue(); } @Test publicvoidisAnyEmpty(){ assertThat(StringUtil.isAnyEmpty(strNull,strEmpty,strSome)) .isTrue(); assertThat(StringUtil.isAnyEmpty()) .isFalse(); } @Test publicvoidisAllEmpty(){ assertThat(StringUtil.isAllEmpty(strNull,strEmpty,strSome)) .isFalse(); assertThat(StringUtil.isAnyEmpty(strNull,strEmpty)) .isTrue(); } }
这里和非SpringBoot测试时没什么太大的区别,唯一的一点就是引入Jar不同,这里虽然我们只引入了spring-boot-starter-test,但它本身已经帮我们引入了许多的测试相关类库了。
Dao/Service测试
从这里开始就和标准的Spring不太一样了
首先,我们需要Dao层,这里使用H2DB和SpringJDBC做数据访问层(比较简单)。
依赖
com.h2database h2 runtime org.springframework.boot spring-boot-starter-jdbc
添加两个初始化脚本
数据库结构db_schema.sql(db/db_schema.sql)
droptableifexistsuser; createtableuser( idintauto_incrementnotnull comment'编号', namevarchar(20)notnull comment'名字', sexbooleannull comment'性别', ageintnull comment'年龄' );
数据库数据db_data.sql(db/db_data.sql)
insertintouser(id,name,sex,age) values (1,'琉璃',false,17), (2,'月姬',false,1000);
为SpringBoot配置一下数据源及初始化脚本
spring: datasource: driver-class-name:org.h2.Driver platform:h2 schema:classpath:db/db_schema.sql data:classpath:db/db_data.sql
然后是实体类与Dao
用户实体类User(com.rxliuli.example.springboottest.entity.User)
/** *@authorrxliuli */ publicclassUserimplementsSerializable{ privateIntegerid; privateStringname; privateBooleansex; privateIntegerage; publicUser(){ } publicUser(Stringname,Booleansex,Integerage){ this.name=name; this.sex=sex; this.age=age; } publicUser(Integerid,Stringname,Booleansex,Integerage){ this.id=id; this.name=name; this.sex=sex; this.age=age; } //getter()andsetter() }
用户DaoUserDao(com.rxliuli.example.springboottest.dao.UserDao)
/** *@authorrxliuli */ @Repository publicclassUserDao{ privatefinalRowMapperuserRowMapper=(rs,rowNum)->newUser( rs.getInt("id"), rs.getString("name"), rs.getBoolean("sex"), rs.getInt("age") ); @Autowired privateJdbcTemplatejdbcTemplate; /** *根据id获取一个对象 * *@paramidid *@return根据id查询到的对象,如果没有查到则为null */ publicUserget(Integerid){ returnjdbcTemplate.queryForObject("select*fromuserwhereid=?",userRowMapper,id); } /** *查询全部用户 * *@return全部用户列表 */ publicList listForAll(){ returnjdbcTemplate.query("select*fromuser",userRowMapper); } /** *根据id删除用户 * *@paramid用户id *@return受影响行数 */ publicintdeleteById(Integerid){ returnjdbcTemplate.update("deletefromuserwhereid=?",id); } }
接下来才是正事,测试Dao层需要加载Spring容器,自动回滚以避免污染数据库。
/** *{@code@SpringBootTest}和{@code@RunWith(SpringRunner.class)}是必须的,这里貌似一直有人误会需要使用{@code@RunWith(SpringJUnit4ClassRunner.class)},但其实并不需要了 *下面的{@code@Transactional}和{@code@Rollback}则是开启事务控制以及自动回滚 * *@authorrxliuli */ @SpringBootTest @RunWith(SpringRunner.class) @Transactional @Rollback publicclassUserDaoTest{ @Autowired privateUserDaouserDao; @Test publicvoidget(){ intid=1; Userresult=userDao.get(id); //断言id和getid相同 assertThat(result) .extracting(User::getId) .contains(id); } @Test publicvoidlistForAll(){ ListuserList=userDao.listForAll(); //断言不为空 assertThat(userList) .isNotEmpty(); } @Test publicvoiddeleteById(){ intresult=userDao.deleteById(1); assertThat(result) .isGreaterThan(0); } }
Web测试
与传统的SpringTest一样,SpringBoot也分为两种。
- 独立安装测试:
手动加载单个Controller,所以测试其他Controller中的接口会发生异常。但测试速度上较快,所以应当优先选择。
- 集成Web环境测试:
将启动并且加载所有的Controller,所以效率上之于BaseWebUnitTest来说非常低下,仅适用于集成测试多个Controller时使用。
独立安装测试
主要是设置需要使用的Controller实例,然后用获得MockMvc对象进行测试即可。
/** *@authorrxliuli */ @SpringBootTest @RunWith(SpringRunner.class) @Transactional @Rollback publicclassUserControllerUnitTest{ @Autowired privateUserControlleruserController; /** *用于测试API的模拟请求对象 */ privateMockMvcmockMvc; @Before publicvoidbefore(){ //模拟一个Mvc测试环境,获取一个MockMvc实例 mockMvc=MockMvcBuilders.standaloneSetup(userController) .build(); } @Test publicvoidtestGet()throwsException{ //测试能够正常获取 Integerid=1; mockMvc.perform( //发起get请求 get("/user/"+id) ) //断言请求的状态是成功的(200) .andExpect(status().isOk()) //断言返回对象的id和请求的id相同 .andExpect(jsonPath("$.id").value(id)); } @Test publicvoidlistForAll()throwsException{ //测试正常获取 mockMvc.perform( //发起post请求 post("/user/listForAll") ) //断言请求状态 .andExpect(status().isOk()) //断言返回结果是数组 .andExpect(jsonPath("$").isArray()) //断言返回数组不是空的 .andExpect(jsonPath("$").isNotEmpty()); } }
集成Web环境测试
/** *@authorrxliuli */ @SpringBootTest @RunWith(SpringRunner.class) @Transactional @Rollback publicclassUserControllerIntegratedTest{ @Autowired privateWebApplicationContextcontext; /** *用于测试API的模拟请求对象 */ privateMockMvcmockMvc; @Before publicvoidbefore(){ //这里把整个WebApplicationContext上下文都丢进去了,所以可以测试所有的Controller mockMvc=MockMvcBuilders.webAppContextSetup(context) .build(); } @Test publicvoidtestGet()throwsException{ //测试能够正常获取 Integerid=1; mockMvc.perform( //发起get请求 get("/user/"+id) ) //断言请求的状态是成功的(200) .andExpect(status().isOk()) //断言返回对象的id和请求的id相同 .andExpect(jsonPath("$.id").value(id)); } @Test publicvoidlistForAll()throwsException{ //测试正常获取 mockMvc.perform( //发起post请求 post("/user/listForAll") ) //断言请求状态 .andExpect(status().isOk()) //断言返回结果是数组 .andExpect(jsonPath("$").isArray()) //断言返回数组不是空的 .andExpect(jsonPath("$").isNotEmpty()); } }
总结
其实上面的测试类的注解感觉都差不多,我们可以将一些普遍的注解封装到基类,然后测试类只要继承基类就能得到所需要的环境,吾辈自己的测试基类在src/test/common下面,具体使用方法便留到下次再说吧
以上代码已全部放到GitHub上面,可以直接clone下来进行测试
到此这篇关于如何用SpringBoot进行测试的文章就介绍到这了,更多相关SpringBoot测试内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!