当前位置: 代码网 > it编程>编程语言>Java > 【SpringBoot】单元测试实战演示及心得分享

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

2024年08月06日 Java 我要评论
从测试用例到技巧,详解在实战中如何进行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<traindetail> traindetaillist = (list<traindetail>) invocation.getarguments()[0];
        assert.assertequals(traindetails.getitems(), traindetaillist);
        return traindetails;
    }).when(traindetaildao).updatelist(any());
    traindetailbasesvr.modifytraindetails("",traindetails);
}

2.设计测试用例

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

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

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

灰色的节点就是要mock掉的

图片

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

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<examination> examinationlist = (list<examination>)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<examination> examinationlist = (list<examination>)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<examination> examinationlist = (list<examination>)invocation.getarguments()[0];
            assert.assertequals(examinationlist,examinations.getitems());
            return 1;
        }).when(examinationdao).updatelist(any());
        examinationbasesvr.deleteexamination("",examinations,false);
    }
}

4.跑测试集

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

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

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com