【SpringBoot】单元测试实战演示及心得分享

06-01 267阅读

【SpringBoot】单元测试实战演示及心得分享

目录

1.指定测试标准

2.设计测试用例

3.测试集示例

4.跑测试集


1.指定测试标准

单元测试会用到mock和junit的内容,作者前文有详解,可移步:

Spring Boot单元测试-CSDN博客

mockito的详细使用-CSDN博客

1.1.测哪一层?

以当前后端标准的MVC分层来说,后端代码分为controller、service、dao三层。首先我们要先确定这三层里面去测试哪一层?单元测试的核心目的是什么:

覆盖业务代码

按标准的来说的话controller层是系统对外暴露的API,这一层级只负责做一些请求和参数的处理;service层用来编写具体的业务逻辑;dao层负责与数据库进行交互。所以我们应该测service层。

1.2.如何判断测试是否通过?

测试的输出结果和我们期望的输出结果是一致的,测试就通过了。怎么判断喃?

用Assert断言

Assert不要到处去用,在测试用例的最后用它来判断一下输出结果是不是期望值即可。

1.3.mock掉哪些内容?

mock我们主要拿来干两件事儿:

  • mock掉对数据库的操作,避免引起数据的改动,也就是说要mock掉dao层的方法

  • mock掉没办法达到的地方,比如有些地方不影响代码逻辑,但是在测试的时候不好造出来,这些不可达的地方可以mock掉。

    mock我们要mock两种情况:

    • mock返回值

    • mock行为

      mock返回值,比如:

      Train train = new Train();
      String id = UUID.randomUUID() + "";
      train.setKeyId(id);
      when(trainDao.getDetail(any(Train.class))).thenReturn(train);

      mock行为有些时候是主动的,我们想去定义实体的具体行为,有时候是被动的,比如要mock的dao方法没有返回值该,我们就只能通过去mock行为来使得它不去操作数据库,反正核心就是不让它去操作数据库。

      比如以下方法:

      void trainDetailDao.updateList(XXX)

      用doAnswer去mock它的响应:

      @Test
      public void modifyTrainDetails(){
          TrainDetailList trainDetails = new TrainDetailList();
          TrainDetail trainDetail = new TrainDetail();
          trainDetail.setKeyId(UUID.randomUUID()+"");
          trainDetails.add(trainDetail);
          doAnswer(invocation -> {
              List trainDetailList = (List) invocation.getArguments()[0];
              Assert.assertEquals(trainDetails.getItems(), trainDetailList);
              return trainDetails;
          }).when(trainDetailDao).updateList(any());
          trainDetailBaseSvr.modifyTrainDetails("",trainDetails);
      }

      2.设计测试用例

      一个接口只需要一个测试用例吗?有时候是不够的。

      衡量对一个接口的单元测试是不是到位了,核心指标是看它的分支覆盖率。代码种的一个方法里面有些时候会存在一些选择分支(带判断性质的语句),我们设计测试用例的时候要考虑覆盖掉所有分支。

      最好的办法就是画个流程图,设计测试用例的时候要覆盖掉所有流程分支,以下以用户买猪肉为一个例子:

      灰色的节点就是要mock掉的

      【SpringBoot】单元测试实战演示及心得分享

      细化成流程图,流程图的所有出口就是要覆盖的分支,有几个出口,就应该有几个用例,有几个测试方法:

      【SpringBoot】单元测试实战演示及心得分享

      3.测试集示例

      以下是作者在工作中编写的一个测试集用例,演示了一个简单的对增删改查方法的覆盖。里面演示了如何覆盖有返回值的方法和没有返回值的方法。

      这里有几个技巧分享一下:

      首先是要mock掉dao层的话,我们就要把service里面依赖的dao换成mock出来的dao,这里需要用反射的方式强行访问到service里面的dao,然后把它替换掉。其次mock掉dao层之后直接new service就行,完全不需要用到自动注入,也就是不需要用到IOC,也就不需要用到@RunWith(XXX.class) @SpringBootTest(classes = XXX.class)之类的注解来启动SpringBoot了。这样跑测试用例的时候,省去了启动时间,会快很多。

      public class ExaminationBaseSvrTest extends PropertyControllerBase {
          IExaminationBaseSvr examinationBaseSvr;
          ExaminationTargetService examinationTargetService;
          private ExaminationDao examinationDao;
          private IDataDicItemBaseMgeSvr dataDicItemBaseMgeSvr;
          private DataDictionaryItemDao dataDictionaryItemDao;
          @Before
          public void setUp() throws Exception{
              examinationBaseSvr = new ExaminationBaseSvr();
              Field field = ExaminationBaseSvr.class.getDeclaredField("examinationDao");
              Field dataDicItemBaseMgeSvrField = ExaminationBaseSvr.class.getDeclaredField("dataDicItemBaseMgeSvr");
              Field dataDictionaryItemDaoField = DataDicItemBaseMgeSvr.class.getDeclaredField("dataDictionaryItemDao");
              field.setAccessible(true);
              dataDicItemBaseMgeSvrField.setAccessible(true);
              dataDictionaryItemDaoField.setAccessible(true);
              examinationDao = mock(ExaminationDao.class);
              dataDictionaryItemDao=mock(DataDictionaryItemDao.class);
              dataDicItemBaseMgeSvr=mock(DataDicItemBaseMgeSvr.class);
              field.set(examinationBaseSvr, examinationDao);
              dataDicItemBaseMgeSvrField.set(examinationBaseSvr,dataDicItemBaseMgeSvr);
              dataDictionaryItemDaoField.set(dataDicItemBaseMgeSvr,dataDictionaryItemDao);
          }
          @Test
          public void addExamination(){
              when(examinationDao.insert(any())).thenReturn(1);
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              Assert.assertEquals(examinationBaseSvr.addExamination("",examination),examination);
          }
          @Test
          public void addExcaminations(){
              ExaminationList examinations = new ExaminationList();
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              examinations.add(examination);
              doAnswer(invocation -> {
                  List examinationList = (List)invocation.getArguments()[0];
                  Assert.assertEquals(examinationList,examinations.getItems());
                  return 1;
              }).when(examinationDao).insertList(any());
              examinationBaseSvr.addExaminations("",examinations);
          }
          @Test
          public void modifyExamination(){
              when(examinationDao.update(any())).thenReturn(1);
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              Assert.assertEquals(examinationBaseSvr.modifyExamination("",examination),examination);
          }
          @Test
          public void modifyExaminations(){
              ExaminationList examinations = new ExaminationList();
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              examinations.add(examination);
              doAnswer(invocation -> {
                  List examinationList = (List)invocation.getArguments()[0];
                  Assert.assertEquals(examinationList,examinations.getItems());
                  return 1;
              }).when(examinationDao).updateList(examinations.getItems());
              examinationBaseSvr.modifyExaminations("",examinations);
          }
          @Test
          public void deleteExamination(){
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              when(examinationDao.update(any())).thenReturn(1);
              when(examinationDao.getDetail(any())).thenReturn(examination);
              Assert.assertEquals(examinationBaseSvr.deleteExamination("",examination.getKeyId(),false),1);
          }
          @Test
          public void deleteExaminations(){
              ExaminationList examinations = new ExaminationList();
              Examination examination = new Examination();
              examination.setKeyId(UUID.randomUUID()+"");
              examinations.add(examination);
              doAnswer(invocation -> {
                  List examinationList = (List)invocation.getArguments()[0];
                  Assert.assertEquals(examinationList,examinations.getItems());
                  return 1;
              }).when(examinationDao).updateList(any());
              examinationBaseSvr.deleteExamination("",examinations,false);
          }
      }

      4.跑测试集

      测试类写完之后,类名旁边有一个run的图标,点击即可跑整个测试集。其中有普通的run以及带覆盖率报告的run:

      【SpringBoot】单元测试实战演示及心得分享

      选择带覆盖率的run之后会显示覆盖率:

      【SpringBoot】单元测试实战演示及心得分享

      相看类里面具体是哪些代码段被覆盖了,可以在跑完测试集后进入具体的被测试类,代码行旁边会有颜色条,绿色表示被cover的内容:

      【SpringBoot】单元测试实战演示及心得分享

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码