springboot junit单元测试
mb61f5029be6939 人气:0在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况,比如,代码覆盖率必须达到80%或 90%。于是乎,测试人员费尽心思设计案例覆盖代码。用代码覆盖率来衡量,有利也有弊。
首先,让我们先来了解一下所谓的“代码覆盖率”。我找来了所谓的定义:代码覆盖率 = 代码的覆盖程度,一种度量方式。
一:查看jar包版本号是否为junit4;
junit自身注解:
@BeforeClass 全局只会执行一次,而且是第一个运行 @Before 在测试方法运行之前运行 @Test 测试方法 @After 在测试方法运行之后允许 @AfterClass 全局只会执行一次,而且是最后一个运行 @Ignore 忽略此方法
坑:为什么要先看版本号因为在junit5中@Before无效需要使用@BeforeEach;
二:实战应用:
idea快捷创建测试类:
1.首先要保证有test类,和main同级:
2.创建
编辑
添加图片注释,不超过 140 字(可选)
3.编写单元测试
@RunWith(SpringRunner.class):运行器指定 @SpringBootTest(classes =IotSystemApplication.class, webEnvironment =SpringBootTest.WebEnvironment.DEFINED_PORT) @Slf4j
可选参数
@ActiveProfiles("baseline") :表示项目启动参数为-baseline @Transactional :回滚
import com.shimao.iot.common.entity.ResultListVO; import com.shimao.iot.common.entity.ResultVO; import com.shimao.iot.common.model.dict.vo.AreaDictGangVo; import com.shimao.iot.common.model.dict.vo.AreaDictVo; import com.shimao.iot.system.IotSystemApplication; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.jupiter.api.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author zhangtonghao * @create 2022-02-16 11:00 */ @ActiveProfiles("baseline") @RunWith(SpringRunner.class) @SpringBootTest(classes =IotSystemApplication.class, webEnvironment =SpringBootTest.WebEnvironment.DEFINED_PORT) @AutoConfigureMockMvc @Transactional @Slf4j class AreaDictionaryControllerTest { private MockMvc mockMvc; @Autowired private AreaDictionaryController controller; @Autowired private WebApplicationContext webApplicationContext; /*@BeforeEach 初始化数据 void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); }*/ @Test void getRegionDetail() { ResultListVO<AreaDictGangVo> regionDetail =controller.getRegionDetail(""); System.out.println("======测试成功:" +regionDetail); String name ="华北"; regionDetail =controller.getRegionDetail(name); Assert.assertNotNull(regionDetail); System.out.println("======测试成功:" +regionDetail); } @Test void testGetRegionDetail() { Long areaCode =100001L; ResultVO<AreaDictVo> regionDetail =controller.getRegionDetail(areaCode); Assert.assertNotNull(regionDetail); System.out.println("======测试成功:" +regionDetail); } @Test void getAreaTypeDetails() { Integer areaCode =100001; ResultListVO<AreaDictVo> resultListVO =controller.getAreaTypeDetails(areaCode); Assert.assertNotNull(resultListVO); System.out.println("======测试成功:" +resultListVO); } @Test void getSubrangeDetail() { Long areaCode =100001L; ResultListVO<AreaDictVo> resultListVO =controller.getSubrangeDetail(areaCode); Assert.assertNotNull(resultListVO); System.out.println("======测试成功:" +resultListVO); } @Test void getGangDetail() { Long areaCode =100001L; ResultVO<AreaDictGangVo> resultListVO =controller.getGangDetail(areaCode); Assert.assertNotNull(resultListVO); System.out.println("======测试成功:" +resultListVO); } @Test void findAreaDictList() { Long areaCode =100001L; List<Long> areaCodes =new ArrayList<>(); areaCodes.add(areaCode); List<AreaDictVo> resultListVO =controller.findAreaDictList(areaCodes); Assert.assertNotNull(resultListVO); System.out.println("======测试成功:" +resultListVO); } @Test void findAreaDictParentMap() { Long areaCode =100001L; List<Long> areaCodes =new ArrayList<>(); areaCodes.add(areaCode); Map<Long, AreaDictVo> resultListVO =controller.findAreaDictParentMap(areaCodes); Assert.assertNotNull(resultListVO); System.out.println("======测试成功:" +resultListVO); } @Test void queryProvinceAndCity() { ResultListVO<AreaDictVo> resultListVO =controller.queryProvinceAndCity(); Assert.assertNotNull(resultListVO); } }
4.启动测试:正常启动我就不说了说下如何查看覆盖率启动
首先需要安装junit插件,然后启动:
注意点:其实书写单元测试没有什么技巧,只有一个参数问题;你能保证参数正确就可以让测试覆盖率更高。这都需要看代码实现,所以不用妄想一键生成覆盖率达到百分之80啥的了,不存在的。下面还有一些可用的技巧和讲解,着急的可以不用看直接回去写代码去了。
三:扩展
某些对象类的单元测试:
/** * 调用VO DTO POJO MODEL等服务通用方法 */ @Test public void testDto() { HeartDtoheartDto = newHeartDto(); testGetAndSet(heartDto); heartDto.toString(); heartDto.equals(newObject()); heartDto.hashCode(); AlllogDtoallLogDto = newAlllogDto(); testGetAndSet(allLogDto); allLogDto.toString(); allLogDto.equals(newObject()); allLogDto.hashCode(); } /** * JavaBean属性名要求:前两个字母要么都大写,要么都小写 * 对于首字母是一个单词的情况,要么过滤掉,要么自己拼方法名 * f.isSynthetic()过滤合成字段 */ private static void testGetAndSet(Object t) { try { Class modelClass =t.getClass(); Object obj =modelClass.newInstance(); Field[] fields =modelClass.getDeclaredFields(); for (Fieldf :fields) { if (f.getName().equals("aLike") ||f.isSynthetic()) { continue; } PropertyDescriptorpd = newPropertyDescriptor(f.getName(), modelClass); Methodget =pd.getReadMethod(); Methodset =pd.getWriteMethod(); set.invoke(obj, get.invoke(obj)); } } catch (Exception e) { e.printStackTrace(); } }
断言列表:
-------------->assertTrue(String message, boolean condition) 要求condition == true -------------->assertFalse(String message, boolean condition) 要求condition == false -------------->assertEquals(String message, XXX expected,XXX actual) 要求expected期望的值能够等于actual -------------->assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual) -------------->assertNotNull(String message, Object object) 要求object!=null -------------->assertNull(String message, Object object) 要求object==null -------------->assertSame(String message, Object expected, Object actual) 要求expected == actual -------------->assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual -------------->assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true -------------->fail(String message) 要求执行的目标结构必然失败,同样要求代码不可达,即是这个方法在程序运行后不会成功返回,如果成功返回了则报错
代码覆盖程度的度量方式是有很多种的,这里介绍一下最常用的几种:
1. 语句覆盖(StatementCoverage)
又称行覆盖(LineCoverage),段覆盖(SegmentCoverage),基本块覆盖(BasicBlockCoverage),这是最常用也是最常见的一种覆盖方式,就是度量被测代码中每个可执行语句是否被执行到了。这里说的是“可执行语句”,因此就不会包括像C++的头文件声明,代码注释,空行,等等。非常好理解,只统计能够执行的代码被执行了多少行。需要注意的是,单独一行的花括号{} 也常常被统计进去。语句覆盖常常被人指责为“最弱的覆盖”,它只管覆盖代码中的执行语句,却不考虑各种分支的组合等等。假如你的上司只要求你达到语句覆盖,那么你可以省下很多功夫,但是,换来的确实测试效果的不明显,很难更多地发现代码中的问题。
2. 判定覆盖(DecisionCoverage)
又称分支覆盖(BranchCoverage),所有边界覆盖(All-EdgesCoverage),基本路径覆盖(BasicPathCoverage),判定路径覆盖(Decision-Decision-Path)。它度量程序中每一个判定的分支是否都被测试到了。这句话是需要进一步理解的,应该非常容易和下面说到的条件覆盖混淆。因此我们直接介绍第三种覆盖方式,然后和判定覆盖一起来对比,就明白两者是怎么回事了。
3. 条件覆盖(ConditionCoverage)
它度量判定中的每个子表达式结果true和false是否被测试到了。
4. 路径覆盖(PathCoverage)
又称断言覆盖(PredicateCoverage)。它度量了是否函数的每一个分支都被执行了。这句话也非常好理解,就是所有可能的分支都执行一遍,有多个分支嵌套时,需要对多个分支进行排列组合,可想而知,测试路径随着分支的数量指数级别增加。
加载全部内容