文章目录

  • 前言
  • 一、单元测试
    • 1.单元测试是什么?
    • 2.为什么需要单元测试?
  • 二、Junit
    • 1.什么是junit
    • 2.Junit概念——断言
    • 3.Junit的简单使用
      • ①导入依赖
      • ②编写测试用例
    • 4.SpringBoot环境下的junit使用
      • ①导入依赖
      • ②编写测试用例
  • 三、模拟数据——mockito框架的使用
    • 1.mock
    • 2.打桩Stub
    • 3.@MockBean和@SpyBean

前言

这学期有软件测试的课,课程作业便是写一些测试工具的实验报告,这确实是一个不错的机会,毕竟作为全栈工程师,怎么会连测试都不知道呢(笑哭)。

本文简单讲述了单元测试和Java的单测工具junit,以及配合mokito框架的简单使用。另外,我会将其他测试相关的文章也放在这个系列。

一、单元测试

1.单元测试是什么?

借用一下百度百科的话,

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

这里有几个关键点:

  • 单元是人为规定的
  • 单元测试是独立单元,要和其他部分相分离。

2.为什么需要单元测试?

这里谈谈我自己的感受,就我自己而言,是没有单元测试的习惯的,我感觉单元测试会非常耗费时间,同时我认为这些时间花费的不太值得,因为在我初学阶段,做的都是一些简单的crud项目。
但是随着我开发的项目越来越大,需求越来越复杂,我渐渐发现我做的项目质量越来越不稳定,常常会出现一些奇怪的bug。当出现bug时,我们往往要定位问题的所在。就比如前段时间我自己做的一个通用物联网平台,在录制演示视频时输入矫正公式(支持四则运算),当时在其他设备下都能正常运作,但偏偏那次出现了异常。好在整个项目都是自己做的,对于一些实现细节上也都心里有数,调试了一会后发现是算法问题,那时我才猛然想起自己写的时候明白这个算法实现不支持负数,如果要进行负数运算得变成“(0-x)”的形式,而恰巧那台设备上传的数据是负数,所以出现了问题。

好在是全栈开发,所有东西都是自己做的,如果这个项目是团队开发,我估计定位bug的所耗费的时间将会指数级增长。

正因为在集成测试等大规模测试中,定位bug所耗费的时间实在是太长了,所以我们需要单元测试来保证每个小模块的正确性。 尽管它会耗费更多的时间,但是这些时间比起后期层出不穷的bug以及解决bug所耗费的是时间,这些都是值得的。

在开发项目的过程中,很多时候都是在解决之前的bug遗留。

我在网上看到过相关的总结,写的非常好分享一下——单元测试到底是什么?应该怎么做?

  • 单元测试对我们的产品质量是非常重要的。
  • 单元测试是所有测试中最底层的一类测试,是第一个环节,也是最重要的一个环节,是唯一一次有保证能够代码覆盖率达到100%的测试,是整个软件测试过程的基础和前提,单元测试防止了开发的后期因bug过多而失控,单元测试的性价比是最好的。
  • 据统计,大约有80%的错误是在软件设计阶段引入的,并且修正一个软件错误所需的费用将随着软件生命期的进展而上升。错误发现的越晚,修复它的费用就越高,而且呈指数增长的趋势。
  • 作为编码人员,也是单元测试的主要执行者,是唯一能够做到生产出无缺陷程序这一点的人,其他任何人都无法做到这一点代码规范、优化,可测试性的代码
  • 放心重构
  • 自动化执行three-thousand times

二、Junit

1.什么是junit

JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中为最成功的一个。 JUnit有它自己的JUnit扩展生态圈。
目前junit已经发展到了junit5,相较于junit4有了很大的改变。JUnit5由来自三个不同子项目的几个不同模块组成。
JUnit 5=JUnit平台+JUnit Jupiter+JUnit Vintage

详见官网

注:junit基本上Java单元测试的主流,现今大多数Java项目都有junit的身影

2.Junit概念——断言

刚接触过单元测试的同学在学习junit时肯定会疑惑assert方法到底是什么意思,什么叫断言。我一开始接触时就是这样,疑惑断言是干嘛的。

其实断言其实是一些辅助函数,他们用来帮助我们确定被测试的方法是否按照预期的效果正常工作,通常,把这些辅助函数称为断言。

3.Junit的简单使用

以下演示为maven项目

①导入依赖

<dependencies><!-- ... --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.1</version><scope>test</scope></dependency><!-- ... -->
</dependencies>
<build><plugins><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.2</version></plugin><plugin><artifactId>maven-failsafe-plugin</artifactId><version>2.22.2</version></plugin></plugins>
</build>

②编写测试用例

这里引用官网上的例子

import static org.junit.jupiter.api.Assertions.assertEquals;import example.util.Calculator;import org.junit.jupiter.api.Test;class MyFirstJUnitJupiterTests {private final Calculator calculator = new Calculator();@Testvoid addition() {assertEquals(2, calculator.add(1, 1));}}

上面这个例子就是断言了calculator.add(1, 1)的返回值会等于2。

在idea中运行测试将会很方便,只需点击运行图标即可

如果不是idea中,也只需加个mian函数运行即可。

如果运行断言正确,那么程序会如下:

如果断言错误,junit会给你抛出一个AssertionFailedError异常,并告诉你出错的情况

4.SpringBoot环境下的junit使用

当然我们在实际开发中,比如在SpringBoot环境下开发,这时很多业务代码类都是被注入到Spring容器,而类之间又有其他注入类的依赖,像之前那样创建一个测试对象显然不现实。那有什么办法能解决这个问题呢?
下面我来介绍一下junit在SpringBoot+SSM项目中的使用。

①导入依赖

在SpringBoot中它将依赖进行整合,如果我们需要测试的相关依赖,只需引入对应的测试模块即可

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions>
</dependency>

②编写测试用例

测试对象类

package com.example.demo.service;import org.springframework.stereotype.Component;@Component
public class Junit5Test {public int add(int i,int j){System.out.println("-----------add被执行了---------------");return i+j;}public int doAdd(int i,int j){System.out.println("------------doAdd被执行了--------------");//被mock的函数会先执行,且只会执行一次System.out.println(add(i,j));return add(i,j);}
}

测试用例

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {@AutowiredJunit5Test junit5Test;//会初始化一次@BeforeAllstatic void init(){System.out.println("init");}//所有测试方法前都会执行一遍@BeforeEachvoid each(){System.out.println("each");}@Testvoid getDeviceStatistic() {Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));}
}

如果需要SpringBoot上下文环境只需在其上加个@SpringBootTest注解即可,当然在老项目中我们可能会看到@RunWith(SpringRunner.class)这种写法。前者是junit5的写法,后者是junit4的写法。

当我们需要spring容器中的测试对象时,我们只需正常注入即可。

@Autowired
Junit5Test junit5Test;

三、模拟数据——mockito框架的使用

1.mock

在实际开发进行单测时,我们测试对象很可能需要请求网络数据或者改变数据库,可是我们又不想让它去变化,这时我们可以使用mockito框架来对数据进行mock。

所谓的mock,就是指,如果我们写的代码依赖于某些对象,而这些对象又很难手动创建(即不知道如何初始化等,像HttpRequest等对象),那么就用一个虚拟的对象来测试。因为它传入的是一个class文件,所以static代码块还是会被运行,但构造函数,实例代码块都不会被执行

2.打桩Stub

所谓打桩Stub,就是用来提供测试时所需要的测试数据,因为是mock的对象,所以可能有些方法并不能知道返回值,因此我们需要去假定返回值。可以对各种交互设置相应的回应,即对方法设置调用返回值,使用when(…).thenReturn(…)和doReturn(…).when(…)。

比如:


//You can mock concrete classes, not only interfacesLinkedList mockedList = mock(LinkedList.class);//stubbingwhen(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());
  • doReturn().when()是无副作用的。打桩的同时不会执行方法。
  • when().thenReturn()是有副作用的,其副作用是指在打桩的同时会先执行一遍方法,这时可能会造成一定的副作用。

3.@MockBean和@SpyBean

当然在SpringBoot的环境下也可以直接@SpyBean和@MockBean注解来替代@Autowired的注入对象,这样就有了一个虚拟的对象。

@MockBean
如果仅使用@MockBean,会将修饰的对象mock掉,这样Junit5Test的add()方法就不再执行具体的细节,但是MockBean会将目标对象的所有方法全部mock,所以test不能真实地被执行,也就无法测试了。

@SpyBean
而有些情况我们又需要执行真实的方法,我们只想对某些方法进行mock,这时就可以使用@SpyBean。
使用@SpyBean修饰的spyJunit5Test是一个真实对象,仅当when(spyJunit5Test.add(1,1)).thenReturn(2);时,add方法被打桩,其他的方法仍被真实调用。

以下是示例

package com.example.demo.service;import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.mock.mockito.SpyBean;import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;//初始化一个spring的上下文,使其可以使用一些注入(junit5)。junit4会用runwith
@SpringBootTest
class Junit5TestTest {//    @Autowired
//    Junit5Test junit5Test;//介于@Autowired和@MockBean之间的注解,当配置了when时使用mock,没有进行打桩则走正常方法@SpyBeanJunit5Test spyJunit5Test;//完全使用mock方法,方法都不会去真正执行。当调用mockBean修饰的方法时,不会去真正执行该方法,只会返回打桩后的值,如果没有打桩时会返回默认值,比如int就返回0。
//    @MockBean
//    Junit5Test mockJunit5Test;//会初始化一次@BeforeAllstatic void init(){System.out.println("init");}//所有测试方法前都会执行一遍@BeforeEachvoid each(){System.out.println("each");}@Testvoid getDeviceStatistic() {Assertions.assertEquals(1,1);}@Testvoid testDeviceStatisticByMock() {//配置mockwhen(spyJunit5Test.add(1,1)).thenReturn(2);Assertions.assertEquals(2,spyJunit5Test.doAdd(1,1));}
}

最后,祝大家程序员节快乐!

【软件测试】Junit单元测试相关推荐

  1. JUnit单元测试依赖包构建路径错误解决办法

    JUnit单元测试依赖包构建路径错误解决办法: 选中报错的项目文件夹→右击选择属性(ALT+Enter)→java构建路径→库→添加库→JUnit→选择合适的Junit库版本.

  2. Junit单元测试需要知道的一些知识点

    Junit单元测试框架-基于java语言对的主流单元测试框架 @beforeClass-位于数据准备前期或者其他前期准备(测试类调用前) --用于提取代码中的共用部分减少冗余,只能声明注解一次 --必 ...

  3. java中JUnit单元测试的使用方法

    package com.atguigu.java2;import java.sql.Date;import org.junit.Test;/** java中的JUnit单元测试* 步骤:(可以直接写@ ...

  4. JUnit单元测试中的setUpBeforeClass()、tearDownAfterClass()、setUp()、tearDown()方法小结

    编写JUnit单元测试的时候,会用到 setUpBeforeClass().tearDownAfterClass().setUp().tearDown()这四个方法,例如用 eclipse新建一个ju ...

  5. SSM中进行Junit单元测试时无法注入service

    场景 在SSM项目中进行Junit单元测试时调用外部的service时,在使用时打断点发现为空. 代码如下: public class AlipayTester {private PassOrderS ...

  6. surefire 拉起 junit 单元测试类 源码阅读(一)

    根据surefire 拉起Junit单元测试类 输出的报错日志 跟踪执行过程: 日志1: java.lang.reflect.InvocationTargetExceptionat sun.refle ...

  7. junit单元测试断言_简而言之,JUnit:单元测试断言

    junit单元测试断言 简而言之,本章涵盖了各种单元测试声明技术. 它详细说明了内置机制, Hamcrest匹配器和AssertJ断言的优缺点 . 正在进行的示例扩大了主题,并说明了如何创建和使用自定 ...

  8. spring junit单元测试

    项目是有很多个功能块组成的,我们开发的时候,当我们开发出来一个功能,想要测试这个功能是否正确,不可能等到前端和后端全部写好了再进行测试,这样太浪费时间,有没有什么方法能直接测试后台的功能写的是否正确( ...

  9. Spring框架 JdbcTemplate类 @Junit单元测试,可以让方法独立执行 如:@Test

    1 package cn.zmh.PingCe; 2 3 import org.junit.Test; 4 import org.springframework.jdbc.core.BeanPrope ...

  10. 七、Web服务器——Junit单元测试 反射 注解学习笔记

    今日内容 1. Junit单元测试 2. 反射 3. 注解 Junit单元测试: * 测试分类:1. 黑盒测试:不需要写代码,给输入值,看程序是否能够输出期望的值.2. 白盒测试:需要写代码的.关注程 ...

最新文章

  1. 巴菲特:买进你同学的10%
  2. 炼油机出来的什么油_轮胎炼油出来的炭黑有什么用?一吨废旧轮胎出多少炭黑...
  3. Delphi 中Tform的visible属性详解
  4. 三层交换机有什么优势?
  5. Chrome跨域问题
  6. 邮箱附件、QQ、微信等社交工具大文件传输解决方案
  7. win10 mbr下装linux,(MBR模式)Win10下安装Ubuntu18.04双系统
  8. 检验mysql安装成功win7,手把手教你win7系统成功安装 RMySQL的处理对策
  9. vs2012 使用mysql_vs2012连接mysql
  10. linux是用于汇编语言,深入理解程序设计:使用Linux汇编语言
  11. 如何克隆服务器系统数据,Linode面板clone克隆功能实现服务器数据完整迁移
  12. MySQL中数据表的查操作
  13. nodewebkitV0.21.6版本的学习
  14. tx2 安装 Anaconda
  15. flex 调用 flash影片剪辑
  16. SeleniumWebDriver驱动2345浏览器
  17. scala 打印乘法口诀表
  18. 计算机应用基础考试题2020上机东华大学,2020年秋东华大学继续教育《大学英语3》平时作业.docx...
  19. 【微机原理】8088/8086CPU引脚
  20. 《让子弹飞》系列——《让子弹飞》中最大的彩蛋

热门文章

  1. Lombok不生效 报错Error: java:找不到符号
  2. latex 分布符号_常用数学符号的 LaTeX 表示方法
  3. linux反编译unity手游,Unity3D安卓手游逆向
  4. 基于鲸鱼算法优化的Elman神经网络数据预测-附代码
  5. protues仿真微处理器8086实现交通灯
  6. 【ESP32最全学习笔记(协议篇)——2.ESP32 LoRa】
  7. Java|Socket编程指南
  8. Oracle笔记之存储过程和函数
  9. 创建(导入)PCB封装库
  10. 使用ffmpeg查询输入流的编码格式(hevc、h264等)