Service层代码单元测试以及单元测试如何Mock

news2024/11/20 8:37:03

一、背景

接着上一篇文章:单元测试入门篇,本篇文章作为单元测试的进阶篇,主要介绍如何对Springboot Service层代码做单元测试,以及单元测试中涉及外调服务时,如何通过Mock完成测试。

二、Springboot Service层代码单元测试

现在项目都流行前后端代码分离,后端使用springboot框架,在service层编写接口代码实现逻辑。假设现在前端不是你写的,你要对你自己写的后端springboot service层提供的接口方法做单元测试,以确保你写的代码是能正常工作的。

Service层代码单元测试:一个简单的service调mapper查询数据库replay_bug表数据量的接口功能

ReplayBugServiceImpl类代码:

@Service
public class ReplayBugServiceImpl implements ReplayBugService {

    @Autowired
    ReplayBugMapper replayBugMapper;

    @Override
    public int queryBugTotalCount() {
        return replayBugMapper.queryBugTotalCount();
    }
}

replayBugMapper.queryBugTotalCount代码:

@Select("select count(1) from replay_bug")
int queryBugTotalCount();

单元测试ReplayBugServiceImplTest类代码:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class ReplayBugServiceImplTest{
    @Autowired
    ReplayBugServiceImpl replayBugService;

    @Test
    public void queryBugTotalCount() {
        int bugCount=replayBugService.queryBugTotalCount();
        System.out.println("结果是:"+bugCount);
    }
}

代码很简单,调用这个接口服务,打印输出,测试是否能正确查出数据。其中关键的两个注解解释:

@RunWith(SpringRunner.class)注解:是一个测试启动器,可以加载SpringBoot测试注解。
让测试在Spring容器环境下执行。如测试类中无此注解,将导致service、dao等自动注入失败。

@SpringBootTest注解:目的是加载ApplicationContext,启动spring容器。

测试结果如下:

在这里插入图片描述

更进一步,测试带入参的service接口:新增接口单元测试

import com.test.service.BestTest;
import com.test.domain.UrlWhiteListVO;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReplayUrlWhiteListServiceImplTest{
    @Autowired
    ReplayUrlWhiteListServiceImpl replayUrlWhiteListService;

    private UrlWhiteListVO urlWhiteListVO;

    @Before
    public void setup(){
        urlWhiteListVO=new UrlWhiteListVO();
        urlWhiteListVO.setAppId(78);
        urlWhiteListVO.setAppName("testAPP");
        urlWhiteListVO.setUrlWhite("http://www.baidu.com");
        urlWhiteListVO.setRemarks("测试一下");
    }

    @Test
    public void save() {
        System.out.println("测试结果:"+replayUrlWhiteListService.save(urlWhiteListVO));
    }
}

比之前多了一个@Before注解,下面自行设置不同的参数值,测试是否在各种入参情况下接口代码都没有问题。

单元测试结果:

在这里插入图片描述

数据库检查数据插入成功:

在这里插入图片描述

三、单元测试使用Mockito完成Mock测试

实际业务代码中可能会调到其他第三方接口、会和数据库有交互,如果要测试跑通一个场景,准备数据会非常麻烦。而单元测试很多时候,我们只关心自己的代码逻辑是否有漏洞,这个使用就需要用到Mock, 不真实调用,而是将外调的接口、数据库层面都Mock返回自己想要的各类假数据。

因此再进一步,单元测试使用Mockito完成Mock测试:

import com.test.dao.ReplayBugMapper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@RunWith(SpringRunner.class)
@SpringBootTest
public class ReplayBugServiceImplMockTest {

    /**
     * 使用@Autowired是让实例对象正常注入
     * 使用@InjectMocks是为了向里面添加@Mock注入的对象
     * */
    @Autowired
    @InjectMocks
    ReplayBugServiceImpl replayBugService;

    @Mock
    ReplayBugMapper replayBugMapper;
    
    @Before
    public void setup() {
        //添加Mock注解初始化
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void queryBugTotalCount() {
        int count=1;
        Mockito.when(replayBugMapper.queryBugTotalCount()).thenReturn(count);

        int bugCount=replayBugService.queryBugTotalCount();
        System.out.println("Mock单元测试返回的结果是:"+bugCount);
    }
}

同样的接口,之前真实调用数据库的时候,我们看到返回的结果是3。本次Mock测试代码中我们定义了count为1,使用Mockito让数据库调用直接Mock返回我们定义的1,不再真实调用数据库。

测试结果:

在这里插入图片描述

Mockito介绍:Mockito是一款用于java开发的mock测试框架,用于快速创建和配置mock对象。通过创建外部依赖的 Mock 对象, 然后将此 Mock 对象注入到测试类中,简化有外部依赖的类的测试。

Mockito使用:在项目pom.xml中引入依赖spring-boot-starter-test,内部就依赖了Mockito

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
</dependency>

测试代码中用到Mockito的注解作用解释:

@InjectMocks:让@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
@Mock:对函数的调用均执行mock,不执行真实调用。

如果只想对某一些外调做mock,其他的外调都走真实调用:

比如Service ReplayServiceImpl中方法如下

public int addBug(ReplayVO replayVO) {
    if(replayManageMapper.addBug(replayVO.getId())==1){
        //判断如果replay_bug表中已经有这条数据,不再重复添加。应对场景是用户多次点击标记记录为待解决bug。
        if(replayBugService.existBugRecords(replayVO)>=1){
            log.info("replay_bug表中数据已存在,不再重复插入数据");
            return 1;
        }else{
            log.info("向replay_bug表中插入数据");
            return replayManageMapper.saveToReplayBug(replayVO.getAppId(),replayVO.getRequestId(),replayVO.getId(),replayVO.getAppName(),replayVO.getSysDomain(),replayVO.getSysUrl(),replayVO.getUserAccount(),replayVO.getParameters(),replayVO.getResponse(),replayVO.getReplayStatus(),CommonUtils.convertDateToTime(replayVO.getReplayTime()));
        }
    }else{
        return 0;
    }
}

第一步先调用replayManageMapper.addBug对replay表中的这条数据更新状态,更新成功后返回1。
第二步再调用replayBugService.existBugRecords判断replay_bug表中是否存在该条记录,如果存在就不再重复插入。
第三步如果不存在,就再调用replayManageMapper.saveToReplayBug,向replay_bug表中插入该条记录。
现在的需求是单元测试时,对第二步外调的其他接口服务replayBugService做Mock处理,对数据库相关的操作不做Mock,真实调用。

单元测试代码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ReplayServiceImplTest {

    private ReplayVO replayVO;
    
    @Autowired
    @InjectMocks
    ReplayServiceImpl replayService;

    @Mock
    ReplayBugService replayBugService;

    @Before
    public void setUp() {

        //添加Mock注解初始化
        MockitoAnnotations.initMocks(this);

        replayVO=new ReplayVO();
        replayVO.setAppId(1);
        replayVO.setRequestId(2);
        replayVO.setId(111);
        replayVO.setAppName("testApp");
        replayVO.setSysDomain("www.test.com");
        replayVO.setSysUrl("http://www.test.com/queryList");
        replayVO.setUserAccount("测试人员");
        replayVO.setParameters("{\"userID\":\"123\"}");
        replayVO.setResponse("{\"result\":\"成功\"}");
        replayVO.setReplayStatus(5);
        Date date =new Date();
        replayVO.setReplayTime(date);
    }

    @Test
    public void addBug() {
        Mockito.when(replayBugService.existBugRecords(replayVO)).thenReturn(5);
        System.out.println("返回值:"+replayService.addBug(replayVO));
    }
}

代码解释:

ReplayBugService做Mock处理,所以加了注解@Mock;
ReplayServiceImpl中,由于需要部分外调服务Mock,部分外调服务不Mock,所以需要加上注解@Autowired和@InjectMocks:
使用@Autowired是让实例对象正常注入;
使用@InjectMocks是为了向里面添加@Mock注入的对象;

当replayBugService.existBugRecords(replayVO), Mock返回5,测试结果为:

在这里插入图片描述

当replayBugService.existBugRecords(replayVO), Mock返回0,测试结果为:

在这里插入图片描述

数据库查看,数据成功插入:

在这里插入图片描述

顺带说一下Mockito的@Spy与@Mock区别:

@Spy修饰的外部类,必须是真实存在的,如果没有我们要自己生成创建

Mockito.doReturn(response).when(testService).save(Mockito.any());

@Mock修饰的外部类,是完全模拟出来的,就算项目中没有这个类的实例,也能自己mock出来一个。

比如Spring项目中如果你引入了一个外部的Service:

  • 如果在写单元测试时候,外部的Service能加载到的话就可以使用@Spy注解,因为Spring能为你从外部服务找到这个Service并生成实例注入。
  • 但是如果外部的服务没有部署,那么Spring就不能为你创建实例,就会报错提示你在创建@Spy修饰服务必须要先实例,此时只要用@Mock注解替换@Spy就好了。

最后,如果有很多的类都需要做单元测试,每一个单元测试类的头上都加公共的注解:
@RunWith(SpringRunner.class)
@SpringBootTest
就显得比较麻烦,可以抽出来写成一个Base类,如果Springboot项目有一些公共的配置需要添加也可以放在这个Base类中:

import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class BeseTest {

    @BeforeClass
    public static void init(){
        System.setProperty("server.domain", "test.server.com");
    }
}

然后其他单元测试类使用时继承这个BaseTest类就OK了,不用再每个类都去加公共的注解、配置:

public class ReplayServiceImplTest extends BestTest

================================================================================================
以上就是本次的全部内容,都看到这里了,如果对你有帮助,麻烦点个赞+收藏+关注,一键三连啦~

欢迎下方扫码关注我的vx公众号:程序员杨叔,各类文章都会第一时间在上面发布,持续分享全栈测试知识干货,你的支持就是作者更新最大的动力~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/62470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]JAVA毕业设计快递物流管理(系统+LW)

[附源码]JAVA毕业设计快递物流管理&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

华为云低代码技术:让矿区管理“智变”,一览无遗

摘要&#xff1a;基于华为低代码平台&#xff0c;万洲嘉智复用开发了9个数字化管理功能&#xff0c;成功解决了矿区管理的空区和难点&#xff0c;帮助煤矿园区实现了智能化管控。本文分享自华为云社区《【云享伙伴】第10期&#xff1a;华为云低代码技术&#xff1a;让矿区管理“…

[附源码]JAVA毕业设计-旅游产品销售管理-演示录像2020(系统+LW)

[附源码]JAVA毕业设计-旅游产品销售管理-演示录像2020&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff…

660846-99-1, Ac-Arg-Gly-Lys-AMC

Control for the two step histone deacetylase assay with Ac-Arg-Gly-Lys(Ac)-AMC. Ac-RGK-AMC corresponds to the product of the deacetylase reaction, which is subsequently cleaved by trypsin. Ac-精氨酸- gly - lys (Ac)-AMC两步法组蛋白去乙酰化酶测定对照。Ac-RGK…

直击家居建材采购痛点,数商云采购协同系统招投标功能助力企业招采透明高效

在后疫情时代&#xff0c;各类企业的采购与供应链数字化转型已是大势所趋&#xff0c;家居建材企业也不例外。用数字化技术助力建材产业转型&#xff0c;为供采双方提供线上精准对接服务&#xff0c;才能赋能新时代建材供应链。 建设采购平台已是大势所趋。作为企业价值链管理…

[附源码]Python计算机毕业设计Django南通大学福利发放管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

UEditorPlus v2.7.0发布 开放独立文档,附件样式优化

UEditor是由百度开发的所见即所得的开源富文本编辑器&#xff0c;基于MIT开源协议&#xff0c;该富文本编辑器帮助不少网站开发者解决富文本编辑器的难点。 UEditorPlus 是有 ModStart 团队基于 UEditor 二次开发的富文本编辑器&#xff0c;主要做了样式的定制&#xff0c;更符…

Hoops API参考: 3D Graphics System的Set_Color()函数

void Set_Color(const char* color_spec)渲染一个段的内容时改变使用的颜色。还可以用于选择要在高亮模式中使用的曲面特性&#xff0c;以及用于特殊纹理贴图效果。 具体细节 Set_Color(&#xff09;的通用目标是使用这个颜色进行绘制。你可以之只给颜色命令&#xff0c;也可…

警惕 有种过敏叫“牛奶蛋白过敏”

过敏是一个全球性的健康问题。以此为基础。WHO据统计&#xff0c;过敏已成为世界第六大疾病。世界上有3亿人患有哮喘、特应性皮炎、过敏性鼻炎、胃肠道疾病等疾病。美国每年有4%-6%的儿童患有食物过敏。由于环境和食品添加剂的日益复杂&#xff0c;中国和发达国家的过敏性疾病正…

Java项目:SSM简单医院信息管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 管理员角色&#xff1a; 登录,个人资料管理,用户管理,科室管理,医生管理,患者管理,科室项目管理,患者诊疗记录管理等功能。 医生角色包含以下功…

SpringMVC_第1章

SpringMVC_第1章 文章目录SpringMVC_第1章一、SpringMVC简介1 SpringMVC概述问题导入1.1 SpringMVC概述2 入门案例【重点】问题导入2.1 实现步骤2.2 代码实现【第一步】创建web工程&#xff08;Maven结构&#xff09;【第二步】设置tomcat服务器&#xff0c;加载web工程(tomcat…

[附源码]计算机毕业设计基于Springboot楼盘销售管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

闲人闲谈PS之三十六——项目状态控制

**惯例闲话&#xff1a;**最近感觉时间不够用&#xff0c;脑子有很多想法&#xff0c;但是到下笔却感觉总是下不了手&#xff0c;写完一段&#xff0c;感觉和自己想的差距很大&#xff0c;然后有全部删除…这难道就是传说中年纪大了&#xff0c;手脚不停使唤…这让闲人更加焦虑…

[附源码]计算机毕业设计基于Springboot数字乡村基础治理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【JDBC】------ResultSet(结果集)和常见异常

分享第二十条励志语句 宁可自信&#xff0c;也不要盲目悲观。因为自信是一种力量&#xff0c;即使你的自信有些盲目&#xff0c;也无关大局&#xff0c;你可以在实践中调整心态&#xff0c;找到自己的恰当的位置。如果盲目自卑&#xff0c;你就必然失去一切。 目录 分享第二十…

汇编语言之母逝世,71岁时还和儿子合写神经网络论文

凯瑟琳・布斯被称为汇编语言之母&#xff0c;具体来说就是她创造了第一个“汇编语言”。 在1940年代中期&#xff0c;第一台通用电子计算机最初并没有用于代码的内部存储。如果我们想要用它编程&#xff0c;就要操纵数千个开关和电缆&#xff0c;而这些开关和电缆所在的位置&am…

GDP-L-岩藻糖,鸟苷二磷酰岩藻糖,Guanosine 5′-diphospho-β-L-fucose sodium salt

产品名称&#xff1a;GDP-L-岩藻糖&#xff0c;鸟苷二磷酰岩藻糖 英文名称&#xff1a;Guanosine 5′-diphospho-β-L-fucose sodium salt 英文别名 [(2R,3S,4R,5R)-5-(2-Amino-6-oxo-1,6-dihydro-9H-purin-9-yl)-3,4-dihydroxytetrahydro-2-furanyl]methyl (3S,4R,5S,6S)-3,…

周末小技 | 开发一个Feeds流系统——写扩散模式

点个关注&#x1f446;跟腾讯工程师学技术导语 | 本文主要针对Feeds流进行介绍&#xff0c;将从Feeds流的演变入手&#xff0c;带你一步步了解Feeds流&#xff0c;而后学习如何从开发角度入手&#xff0c;对其进行建模&#xff0c;抽象出Feeds流常见的架构&#xff0c;最终搭建…

Python开发6年,整理的《Python从入门到精通学习笔记》免费下载

前言 首先明确一点&#xff1a;为什么要学习python&#xff1f; 我说几个最主要的。 1.简单易学&#xff0c;入门友好 python其实就是英文句子&#xff0c;你只要能够认识基本的英文单词&#xff0c;你就可以非常熟练地使用python。 &#xff08;文末送读者福利&#xff09…

[附源码]计算机毕业设计水果管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…