Java单元测试浅析(JUnit+Mockito)

news2024/9/28 23:22:44

Java测试我们应该都遇到过,一般我们会被要求做单元测试,来验证我们代码的功能以及效率。

这里来和大家一起探讨下有关单于测试。

什么是单元测试?

是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。

经常与单元测试联系起来的另外一些开发活动包括代码走读(Code review),静态分析(Static analysis)和动态分析(Dynamic analysis)。静态分析就是对软件的源代码进行研读,查找错误或收集一些度量数据,并不需要对代码进行编译和执行。动态分析就是通过观察软件运行时的动作,来提供执行跟踪,时间分析,以及测试覆盖度方面的信息。

单元测试的必要性?

公司要求我们写单元测试,我们会按照公司要求的单元测试率,来进行相关工作的完成;

那么会出现这样一些情况:

  • 感觉写单元测试费力不讨好,有时候写单元测试的时间大于写业务逻辑的时间,需要 mock 一大堆数据。要保证各种覆盖率,特别是分支覆盖率,需要覆盖到所写的每一个分支。
  • 大家写的单元测试质量参差不齐,因为感觉大家都是为了写测试而写测试,而不是真正的 tdd 
  • 迭代频繁,节奏紧凑的环境下,想要做到真正的 tdd ,还是有很大的难度的

确实,这是一个比较常见的问题。

那么我觉得,对于公司而言,需要有一套相关的单元测试规范,哪些是必须要去写单元测试的,分高低的优先级。同时对于单元测试案例,要做到及时的审查,关注其覆盖率,关注其有效性,对于无效的测试要及时剔除和优化。

单元测试的意义

程序代码都是由基本单元不断组合成复杂的系统,底层基本单元都无法保证输入输出正确性,层级递增时,问题就会不断放大,直到整个系统崩溃无法使用。所以单元测试的意义就在于保证基本功能是正常可用且稳定的。而对于接口、数据源等原因造成的不稳定因素,是外在原因,不在单元测试考虑范围之内。

JUnit使用

Controller层单元测试

这里我们以Springboot为例:

1、引入依赖

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

2、代码案例

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class StudentControllerTest {
	
	// 注入Spring容器
    @Autowired
    private WebApplicationContext applicationContext;
    // 模拟Http请求
    private MockMvc mockMvc;

    @Before
    public void setupMockMvc(){
    	// 初始化MockMvc对象
        mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }
    
    /**
     * 新增学生测试用例
     * @throws Exception
     */
    @Test
    public void addStudent() throws Exception{
        String json="{"name":"张三","className":"三年级一班","age":"20","sex":"男"}";
        mockMvc.perform(MockMvcRequestBuilders.post("/student/save")    //构造一个post请求
                    // 发送端和接收端数据格式
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .accept(MediaType.APPLICATION_JSON_UTF8)
                    .content(json.getBytes())
            )
           // 断言校验返回的code编码
           .andExpect(MockMvcResultMatchers.status().isOk())
           // 添加处理器打印返回结果
           .andDo(MockMvcResultHandlers.print());
    }
}

只需要在类或者指定方法上右键执行即可,可以直接充当 postman 工作访问指定 url,且不需要写请求代码,

本案例中构造 mockMVC 对象时,也可以使用如下方式:

@Autowired
private StudentController studentController;
@Before
public void setupMockMvc(){
   // 初始化MockMvc对象
   mockMvc = MockMvcBuilders.standaloneSetup(studentController).build();
}

其中 MockMVC 是 Spring 测试框架提供的用于 REST 请求的工具,是对 Http 请求的模拟,无需启动整个模块就可以对 Controller 层进行调用,速度快且不依赖网络环境。

使用 MockMVC 的基本步骤如下:

  1. mockMvc.perform 执行请求

  2. MockMvcRequestBuilders.post 或 get 构造请求

  3. MockHttpServletRequestBuilder.param 或 content 添加请求参数

  4. MockMvcRequestBuilders.contentType 添加请求类型

  5. MockMvcRequestBuilders.accept 添加响应类型

  6. ResultActions.andExpect 添加结果断言

  7. ResultActions.andDo 添加返回结果后置处理

  8. ResultActions.andReturn 执行完成后返回相应结果

Service层单元测试

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

	@Autowired
    private StudentService studentService;

    @Test
    public void getOne() throws Exception {
    	 Student stu = studentService.selectByKey(5);
         Assert.assertThat(stu.getName(),CoreMatchers.is("张三"));
    }
}

执行结果

DAO层单元测试

代码示例

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

	@Autowired
    private StudentMapper studentMapper;

    @Test
    @Rollback(value = true)
    @Transactional
    public void insertOne() throws Exception {
    	 Student student = new Student();
    	 student.setName("李四");
    	 student.setMajor("计算机学院");
    	 student.setAge(25);
    	 student.setSex('男');
    	 int count = studentMapper.insert(student);
    	 Assert.assertEquals(1, count);
    }
}

 

 其中 @Rollback (value = true) 可以执行单元测试之后回滚所新增的数据,保持数据库不产生脏数据。

异常测试

1、首先我们在service层可以定义一个异常情况

public void computeScore() {
   int a = 10, b = 0;
   int c = a/b;
}

2、我们在service中编写单元测试

@Test(expected = ArithmeticException.class)
    public void computeScoreTest() {
        studentService.computeScore();
    }

执行单元测试也会通过,原因是 @Test 注解中的定义了异常

 查看单元测试的覆盖率

1) 单测覆盖率

测试覆盖率是衡量测试过程工作本身的有效性,提升测试效率和减少程序 bug,提升产品可靠性与稳定性的指标。

统计单元测试覆盖率的意义:

1) 可以洞察整个代码中的基础组件功能的所有盲点,发现相关问题。

2) 提高代码质量,通常覆盖率低表示代码质量也不会太高,因为单测不通过本来就映射出考虑到各种情况不够充分。

3) 从覆盖率的达标上可以提高代码的设计能力。

(2) 在 idea 中查看单元测试覆盖率很简单,只需按照图中示例的图标运行,或者在单元测试方法或类上右键 Run 'xxx' with Coverage 即可。执行结果是一个表格,列出了类、方法、行数、分支覆盖情况。

(3) 在代码中会标识出覆盖情况,绿色的是已覆盖的,红色的是未覆盖的。

(4) 如果想要导出单元测试的覆盖率结果,可以使用如下图所示的方式,勾选 Open generated HTML in browser

 

导出结果:

 

JUnit插件自动生成测试案例

1) 安装插件,重启 idea 生效

(2) 配置插件

 

(3) 使用插件

在需要生成单测代码的类上右键 generate...,如下图所示。

生成结果:

单元测试工具Mockito

简介

Mockito 是一个针对 Java 的 mocking 框架。它与 EasyMock 和 jMock 很相似,但是通过在执行校验什么已经被调用,它消除了对期望行为(expectations)的需要。其它的 mocking 库需要你在执行记录期望行为(expectations),而这导致了丑陋的初始化代码。

Mock 过程的使用前提:

(1) 实际对象时很难被构造出来的

(2) 实际对象的特定行为很难被触发

(3) 实际对象可能当前还不存在,比如依赖的接口还没有开发完成等等。

Mockito 官网:https://site.mockito.org 。Mockito 和 JUnit 一样是专门针对 Java 语言的 mock 数据框架,它与同类的 EasyMock 和 jMock 功能非常相似,但是该工具更加简单易用。

Mockito 的特点:

(1) 可以模拟类不仅仅是接口

(2) 通过注解方式简单易懂

(3) 支持顺序验证

(4) 具备参数匹配器

使用案例

1、在之前的代码中在定义一个 BookService 接口,含义是借书接口,暂且不做实现

public interface BookService {
    Book orderBook(String name);
}

2、在之前的 StudentService 类中新增一个 orderBook 方法,含义是学生预定书籍方法,其中实现内容调用上述的 BookService 的 orderBook 方法。

public Book orderBook(String name) {
   return bookService.orderBook(name);
}

 3、编写单元测试方法,测试 StudentService 的 orderBook 方法

@Test
public void orderBookTest() {
    Book expectBook = new Book(1L, "钢铁是怎样炼成的", "书架A01");
    Mockito.when(bookService.orderBook(any(String.class))).thenReturn(expectBook);
    Book book = studentService.orderBook("");
    System.out.println(book);
    Assert.assertTrue("预定书籍不符", expectBook.equals(book));
}

4、执行结果

 

上述内容并没有实现 BookService 接口的 orderBook (String name) 方法。但是使用 mockito 进行模拟数据之后,却通过了单元测试,原因就在于 Mockito 替换了本来要在 StudentService 的 orderBook 方法中获取的对象,此处就模拟了该对象很难获取或当前无法获取到,用模拟数据进行替代。

 

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

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

相关文章

C/C++开发,无可避免的内存管理(篇四)-智能指针备选

一、智能指针 采用C/C开发堆内存管理无论是底层开发还是上层应用&#xff0c;无论是开发新手&#xff0c;还是多年的老手&#xff0c;都会不自觉中招&#xff0c;尤其是那些不是自己一手经历的代码&#xff0c;要追溯问题出在哪里更是个麻烦事。C/C程序常常会遇到程序突然退出&…

Idea启动多个SpringBoot项目的3种方案

​ 悟纤&#xff1a;师傅&#xff0c;你最近是哪去了&#xff0c;这上班都快1个月了&#xff0c;都没见你踪影&#xff0c;你这是上哪里去放纵去了吗&#xff1f; 师傅&#xff1a;徒儿&#xff0c;你说的这是啥话&#xff0c;是放松&#xff0c;不是放纵&#xff0c;为师严重…

axicom的测试文档

目录&#xff09;SQLpython开放性业务题&#xff08;二选一&#xff09;完整代码SQL 问题描述 SQL&#xff0c; 请根据前一周各产品的总GMV将其分成五类&#xff1a;GMV Top 20%、20%-40%&#xff0c;40%-60%&#xff0c;60%-80%以及Bottom 20%的产品组&#xff0c;请计算这五…

【Java】创建多线程的四种方式

一、方式1&#xff1a;继承Thread类 步骤&#xff1a; 创建一个继承于Thread类的子类重写Thread类的run()方法 ----> 此线程执行的操作声明在方法体中创建当前Thread子类的对象通过实例对象调用start()方法&#xff0c;启动线程 ----> Java虚拟机会调用run()方法 注意…

QuickHMI Hawk R3 Crack

基于网络的 SCADA / HMI 系统 QuickHMI Hawk R3 QuickHMI是一个 100% 基于网络的SCADA/HMI 系统。 得益于HTML5、SVG和Javascript等现代网络技术&#xff0c;可视化可以在任何当前浏览器和设备中显示。作为浏览器的替代品&#xff0c;可以使用“独立查看器”和移动应用程序。 Q…

react react-redux学习记录

react react-redux学习记录1.原理2.怎么用呢2.1 容器组件2.2UI组件2.3 App.jsx3.简化3.1简写mapDispatch3.2 Provider组件的使用3.3整合UI组件和容器组件1.原理 UI组件:不能使用任何redux的api&#xff0c;只负责页面的呈现、交互等。 容器组件&#xff1a;负责和redux通信&…

Orcad原理图放置辅助线的方法

Orcad原理图放置辅助线的方法 设计当中&#xff0c;可以通过放置辅助线来标识信号方向或者对功能模块进行分块标识。 1&#xff09;执行菜单命令“Place-Line”&#xff08;快捷键“ShiftL”&#xff09;&#xff0c;激活放置状态。 2&#xff09;在一个合适的位置单击鼠标左键…

零基础该如何转行Python工程师?学习路线是什么?

最近1年的主要学习时间&#xff0c;都投资到了 python 数据分析和数据挖掘上面来了&#xff0c;虽然经验并不是十分丰富&#xff0c;但希望也能把自己的经验分享下&#xff0c;最近也好多朋友给我留言&#xff0c;和我聊天&#xff0c;问我python该如何学习&#xff0c;才能少走…

字节前端必会面试题(持续更新中)

事件传播机制&#xff08;事件流&#xff09; 冒泡和捕获 谈一谈HTTP数据传输 大概遇到的情况就分为定长数据 与 不定长数据的处理吧。 定长数据 对于定长的数据包而言&#xff0c;发送端在发送数据的过程中&#xff0c;需要设置Content-Length,来指明发送数据的长度。 当…

前端面试题——性能优化 高频

目录 一、CDN的概念 二、CDN的原理 三、懒加载的实现原理 四、对节流与防抖的理解 五、实现节流函数和防抖函数 六、回流与重绘的概念及触发条件 七、如何避免回流与重绘&#xff1f; 八、如何对项目中的图片进行优化&#xff1f; 九、如何⽤webpack来优化前端性能&…

Python 简单可变、复杂可变、简单不可变、复杂不可变类型的copy、deepcopy的行为

copy模块&#xff1a;copy&#xff1a;浅拷贝deepcopy&#xff1a;深拷贝简单可变类型、复杂可变的copy()、deepcopy()&#xff1a;简单不可变、复杂不可变类型的copy()、deepcopy()&#xff1a;结论&#xff1a;对于简单类型的可变类型copy是深拷贝&#xff0c;改变了该拷贝变…

1.FFmpeg-音视频基础

专栏介绍基于最新的FFmpeg5.1.2版本讲解学习, 跟随博主一起学习ffmpeg: 本专栏学习流程为: FFmpeg安装、

Spring之AOP理解及使用

文章目录AOP是什么AOPSpring的通知类型1.Before通知2. AfterReturning通知3.AfterThrowing通知4. After通知5. Around通知动态代理JDK动态代理CGLib动态代理动态代理的代码展示AOP使用切面类的配置最后大家好&#xff0c;我是Leo&#xff01;今天给大家带来的是关于Spring AOP的…

jsp学生成果管理系Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 学生成果管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0…

分布式理论-学习笔记

1 分布式概述 分布式系统是计算机程序的集合&#xff0c;这些程序利用跨多个独立计算节点的计算资源来实现共同的目标。可以分为分布式计算、分布式存储、分布式数据库等。 2 系统模型 2.1 故障模型 Byzantine failure:节点可以任意篡改发送给其他节点的数据ADB:Byzantine fa…

Stacked hourglass networks for human pose estimation代码学习

Stacked hourglass networks for human pose estimation https://github.com/princeton-vl/pytorch_stacked_hourglass 这是一个用于人体姿态估计的模型&#xff0c;只能检测单个人 作者通过重复的bottom-up&#xff08;高分辨率->低分辨率&#xff09;和top-down&#xff0…

乐友商城学习笔记(十五)

无状态登陆原理 在服务器端保存session 无状态不需要session&#xff0c;把登陆状态保存在cookie中 jwtrsa token&#xff1a;登陆时&#xff0c; jwt oath2 jwt&#xff1a;头信息&#xff08;jwt&#xff09; 载荷&#xff08;用户信息&#xff0c;签发人&#xff0c;签发时…

设备驱动模型--存储技术原理分析笔记 基于2.6.43内核

本文为读书笔记&#xff0c;详细内容参考《存储原理技术分析》1- 驱动模型2- 总线类型2.1- 重要数据结构总线bus_type 和 bus_type_private 互相可以找到对方struct bus_type {const char *name;struct bus_attribute *bus_attrs;struct device_attribute *dev_attrs;s…

2023软件测试工程师全新技术栈,吃透这些,起薪就是25k~

相信每个准备软件测试面试的同学&#xff0c;不管你是大学刚毕业&#xff0c;满心憧憬着进入公司实习、非计算机行业转行软件测试、自学测试就业还是培训后就业&#xff0c;都会面临着众多的疑问和不解&#xff0c;那就是该怎么走出着第一步&#xff0c;今天本文一次性告诉你&a…

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU FLASH

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU 32BIT 256KB FLASH 144LQFP【说明】Kinetis K6x MCU系列是一个可扩展的组合&#xff0c;具有不同级别的集成&#xff0c;提供丰富的模拟、通信、定时和控制外设套件&#xff0c;以适应广泛的需求。应用楼宇自动化控制器人…