Java JUnit 单元测试小结

2023-11-06

测试类型

单元测试(Unit test)

单元测试关注单一的类. 它们存在的目的是检查这个类中的代码是否按照期望正确运行.

集成测试(Integration test)

集成测试是检查开发的模块和其他模块整合时是否正常工作.
虽然集成测试的代码影响范围比单元测试要广, 但是集成测试和单元测试一样, 也是针对于开发者而言的.

端到端测试(End-to-End test)

端到端测试是将整个系统作为一个整体, 然后从用户的角度进行测试的.
端到端测试的目的是测试系统在实际使用的是否正常的, 因此通常来说是不需要测试替身的(Test Double)

 

单元测试基本概念

什么是单元测试

单元测试的目的: 测试当前所写的代码是否是正确的, 例如输入一组数据, 会输出期望的数据; 输入错误数据, 会产生错误异常等.
在单元测试中, 我们需要保证被测系统是独立的(SUT 没有任何的 DOC), 即当被测系统通过测试时, 那么它在任何环境下都是能够正常工作的. 编写单元测试时, 仅仅需要关注单个类就可以了. 而不需要关注例如数据库服务, Web 服务等组件.

被测系统

被测系统(System under test, SUT)表示正在被测试的系统, 目的是测试系统能否正确操作.
根据测试类型的不同, SUT 指代的内容也不同, 例如 SUT 可以是一个类甚至是一整个系统.

测试依赖组件(DOC)

被测系统所依赖的组件, 例如进程 UserService 的单元测试时, UserService 会依赖 UserDao, 因此 UserDao 就是 DOC.

测试替身(Test Double)

一个实际的系统会依赖多个外部对象, 但是在进行单元测试时, 我们会用一些功能较为简单的并且其行为和实际对象类似的假对象来作为 SUT 的依赖对象, 以此来降低单元测试的复杂性和可实现性. 在这里, 这些假对象就被称为 测试替身(Test Double).

测试替身有如下 5 种类型:

  • Test stub, 为 SUT 提供数据的假对象.

假设我们的一个模块需要从 HTTP 接口中获取商品价格数据, 这个获取数据的接口被封装为 getPrice 方法. 在对这个模块进行测试时, 我们显然不太可能专门开一个 HTTP 服务器来提供此接口, 而是提供一个带有 getPrice 方法的假对象, 从这个假对象中获取数据.
在这个例子中, 提供数据的假对象就叫做 Test stub.

  • Fake object
    实现了简单功能的一个假对象. Fake object 和 Test stub 的主要区别就是 Test stub 侧重于用于提供数据的假对象, 而 Fake object 没有这层含义.

       Fake object 的最主要的原因就是在测试时某些组件不可用或运行速度太慢, 因而使用 Fake object 来代替它们.

  • Mock object
    用于模拟实际的对象, 并且能够校验对这个 Mock object 的方法调用是否符合预期.

        实际上, Mock object 是 Test stub 或 Fake object 一种, 但是 Mock object 有 Test stub/Fake object 没有的特性, Mock object 可以很灵活地配置所调用的方法所产生的行为,

        并且它可以追踪方法调用, 例如一个 Mock Object 方法调用时传递了哪些参数, 方法调用了几次等.

  • Dummy object

       在测试中并不使用的, 但是为了测试代码能够正常编译/运行而添加的对象. 例如我们调用一个 Test Double 对象的一个方法, 这个方法需要传递几个参数,

       但是其中某个参数无论是什么值都不会影响测试的结果, 那么这个参数就是一个 Dummy object. Dummy object 可以是一个空引用, 一个空对象或者是一个常量等.

       简单的说, Dummy object 就是那些没有使用到的, 仅仅是为了填充参数列表的对象.

  • Test Spy
    可以包装一个真实的 Java 对象, 并返回一个包装后的新对象. 若没有特别配置的话, 对这个新对象的所有方法调用, 都会委派给实际的 Java 对象.

mock 和 spy 的区别是:

mock 是无中生有地生出一个完全虚拟的对象, 它的所有方法都是虚拟的; 而 spy 是在现有类的基础上包装了一个对象, 即如果我们没有重写 spy 的方法, 那么这些方法的实现其实都是调用的被包装的对象的方法.

 

Test fixture

所谓 test fixture, 就是运行测试程序所需要的先决条件(precondition). 即对被测对象进行测试时所需要的一切东西(The test fixture is everything we need to have in place to exercise the SUT). 这个 东西 不单单指的是数据, 同时包括对被测对象的配置, 被测对象所需要的依赖对象等.
JUnit4 之前是通过 setUp, TearDown 方法完成, 在 JUnit4这, 我们可以使用@Before 代替 setUp 方法, @After 代替 tearDown 方法.

注意, @Before 在每个测试方法运行前都会被调用, @After 在每个测试方法运行后都会被调用.

因为 @Before 和 @After 会在每个测试方法前后都会被调用, 而有时我们仅仅需要在测试前进行一次初始化, 这样的情况下, 可以使用@BeforeClass 和@AfterClass 注解.

测试用例(Test case)

在 JUnit 3中, 测试方法都必须以 test 为前缀, 且必须是 public void 的, JUnit 4之后, 就没有这个限制了, 只要在每个测试方法标注 @Test 注解, 方法签名可以是任意的.

 

测试套件

通过 TestSuit 对象将多个测试用例组装成一个测试套件, 测试套件批量运行.
通过@RunWith 和@SuteClass 两个注解, 我们可以创建一个测试套件. 通过@RunWith 指定一个特殊的运行器, 几 Suite.class 套件运行器, 并通过@SuiteClasses 注解, 将需要进行测试的类列表作作为参数传入.

 

JUnit4

HelloWorld 例子

我们已一个简单的例子来快速展示 JUnit4 的基本用法.
首先新建一个名为 JUniTest 的 Maven 工程, 然后添加依赖:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

接着编写测试套件:

import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestJunit {
    @Test
    public void testingCrunchifyAddition() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }

    @Test
    public void testingHelloWorld() {
        assertEquals("Here is test for Hello World String: ", "Hello + World", helloWorld());
    }

    public int addition(int x, int y) {
        return x + y;
    }

    public String helloWorld() {
        String helloWorld = "Hello +" + " World";
        return helloWorld;
    }
}

随后使用测试用例:

public class App {
    public static void main(String[] args) {
        Result result = JUnitCore.runClasses(TestJunit.class);
        for (Failure failure : result.getFailures()) {
            System.out.println(failure.toString());
        }
        if (result.wasSuccessful()) {
            System.out.println("Both Tests finished successfully...");
        }
    }
}

这就是一个完整的 JUnit 测试例子了.

定义测试

一个 JUnit 测试是一个在专用于测试的类中的一个方法, 并且这个方法被 @org.junit.Test 注解标注. 例如:

public class TestJunit {
    @Test
    public void testingCrunchifyAddition() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }
    ...
}

JUnit4 生命周期

JUnit4测试用例的完整的生命周期要经历如下几个阶段:

  • 类级初始化资源处理

  • 方法级初始化资源处理

  • 执行测试用例中的方法

  • 方法级销毁资源处理

  • 类级销毁资源处理

其中, 类级初始化和销毁资源处理在每一个测试用例类这仅仅执行一次, 方法级初始化, 销毁资源处理方法在执行测试用例这的每个测试方法中都会被执行一次.

 

JUnit4 注解

  • @Test (expected = Exception.class) 表示预期会抛出Exception.class 的异常

  • @Ignore 含义是“某些方法尚未完成,暂不参与此次测试”。这样的话测试结果就会提示你有几个测试被忽略,而不是失败。一旦你完成了相应函数,只需要把@Ignore注解删去,就可以进行正常的测试。

  • @Test(timeout=100) 表示预期方法执行不会超过 100 毫秒,控制死循环

  • @Before 表示该方法在每一个测试方法之前运行,可以使用该方法进行初始化之类的操作

  • @After 表示该方法在每一个测试方法之后运行,可以使用该方法进行释放资源,回收内存之类的操

  • @BeforeClass 表示该方法只执行一次,并且在所有方法之前执行。一般可以使用该方法进行数据库连接操作,注意该注解运用在静态方法。

  • @AfterClass 表示该方法只执行一次,并且在所有方法之后执行。一般可以使用该方法进行数据库连接关闭操作,注意该注解运用在静态方法。

@Test 注解

被@Test 标注的方法就是执行测试用例的测试方法, 例如:

public class TestJunit {
    @Test
    public void  myTest() {
        assertEquals("Here is test for Addition Result: ", 30, addition(27, 3));
    }
}

方法myTest 被注解@Test 标注, 表示这个方法是一个测试方法, 当运行测试用例时, 会自动调用这个方法 .

@BeforeClass , @AfterClass, @Before, @After

使用@BeforeClass 和 @AfterClass 两个注解标注的方法会在所有测试方法执行前后各执行一次
使用@Before 和 @After 两个注解标注的方法会在每个测试方法执行前后都执行一次.

TestSuite

如果有多个测试类, 可以合并成一个测试套件进行测试, 运行一个 Test Suite, 那么就会运行在这个 Test Suite 中的所用的测试.
例如:

import org.junit.runner.RunWith;  
import org.junit.runners.Suite;  
import org.junit.runners.Suite.SuiteClasses;  
  
@RunWith( Suite.class )  
@SuiteClasses( { JUnitTest1.class, JUnitTest2.class } )  
public class AllTests {

}

在这个例子中, 我们定义了一个 Test Suite, 这个 Test Suite 包含了两个测试类: JUnitTest1 和 JUnitTest2, 因此运行 这个 Test Suite 时, 就会自动运行这两个测试类了.

本文由 yongshun 发表于个人博客, 采用署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议.
非商业转载请注明作者及出处. 商业转载请联系作者本人
Email: yongshun1228@gmail.com
本文标题为: Java JUnit 单元测试小结
本文链接为: https://segmentfault.com/a/11...

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java JUnit 单元测试小结 的相关文章

  • ciclop读音,购机必备,15种 3D扫描 设备 优缺点汇总

    原标题 购机必备 15种 3D扫描 设备 优缺点汇总 小编总结了市场上常见的15种 可用于3D扫描的软件和设备 并详细列出有关它们的价格 优缺点等特征 以供大家在购机时有比较实用性参考 一 软件选择 根据研究表明 很多人可能不需要买一个3D

随机推荐

  • Android实现倒计时的几种方案

    前言 关于倒计时可以说我们App开发中常见的一种场景了 比如Splash倒计时跳转首页 比如发送短信之后倒计时60秒显示等等 关于倒计时的实现方式 大家可能有不同的做法 这里做一下总结看看你使用的是哪一种呢 一 CountDownTimer
  • 3、Nginx系列之: location和alias的区别

    Nginx系列之 location和alias的区别 一 概述 root 语法 root path 默认值 root html 配置段 http server location if alias 语法 alias path 配置段 loca
  • 【多线程】三种实现方案

    目录 1 多线程中的并发和并行概念 2 多线程中的进程和线程概念 3 多线程的实现方案 3 1 方式1 继承Thread类的方式进行实现 3 2 方式2 实现Runnable接口 3 3 方式3 Callble和Future 可以获取返回结
  • 关于MaxCompute的基本了解

    大数据计算服务 MaxCompute 原名 ODPS 是一种快速 完全托管的 GB TB PB 级数据仓库解决方案 MaxCompute 为您提供了完善的数据导入方案以及多种经典的分布式计算模型 能够更快速的解决海量数据计算问题 有效降低企
  • 【高数】导数存在,导数就连续吗?

    高数 导数存在 导数就连续吗 一 概念理解 二 问题讨论 三 小结 一 概念理解 函数连续 y f x 在 x 0 的某邻域有定义 且满足下式 也就说明 连续意味着 x 0 处 f x 的极限存在 也即 f x 的左极限 右极限 该点函数值
  • 【STM32】【HAL库】遥控关灯1主机

    相关连接 STM32 HAL库 遥控关灯0 概述 STM32 HAL库 遥控关灯1主机 STM32 HAL库 遥控关灯2 分机 STM32 HAL库 遥控关灯3 遥控器 需求 主机需要以下功能 接收来自物联网平台的命令 发送RF433信号给
  • crmeb多商户系统安装(1)

    服务器配置 以阿里云ECS服务器为例 1 在阿里云控制台 云服务器ECS 实例 点击最右侧更多 实力状态 停止 2 选择停止 点击确定 3 输入短信验证码 4 等待服务器状态为 已停止 点击最右侧更多 磁盘和镜像 更换系统盘 5 点击确定
  • 踩坑日记:java.lang.IllegalStateException: For queries with named parameters you need to use provide ...

    今天的错误是这个 java lang IllegalStateException For queries with named parameters you need to use provide names for method para
  • SQLI-labs-第五关和第六关

    目录 1 判断注入点 2 判断当前表的字段数 3 爆库名 4 爆表名 5 爆字段名 6 爆值 知识点 布尔盲注 思路 1 判断注入点 首先 我们看看正常的回显内容 id 1 接着输入 id 1 结果出现语句错误 这里说明存在单引号的闭合错误
  • 【信号分解】基于辛几何模态SGMD实现信号数据分解附Matlab代码

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab仿真内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器 电力系统 信号
  • 【网络工程】如何本地调试微信公众号开发教程(Nginx代理方法)

    目录 前言 目的 通过Nginx代理实现本地调试微信公众号 实现工具 实现步骤 1 启动本地前端项目 2 首先配置Nginx 3 填写app conf内容 把本地前端项目与域名形成映射 4 把app conf加入到Nginx配置中 5 打开
  • vue-cordova修改app图标+文字以及启动图

    前言 在使用 vue cordova 打包成apk的时候 我们一定会根据项目改把app的图标还有显示文字 加载图片也有可能改动 这里来说一说这个 1 修改显示文字 地址 D cordova app config xml 修改name里面的文
  • 鸿蒙相符合的图片,鸿蒙操作系统亮相——目标是未来的物联网

    原标题 鸿蒙操作系统亮相 目标是未来的物联网 本文由什么值得买用户原创 太空铁 咱们好 我是太空铁 这两天 华为开发者大会很热闹 华为接连发布了鸿蒙 凌霄以及才智屏等软硬件产品 其间最让人等待的当属鸿蒙操作体系了 鸿蒙操作体系早前可说是进行
  • 系统环境变量修改后需要重启qt才能响应

    修改系统环境变量 不需要重启系统 但需要重启qt才能感知新的环境变量
  • 物业小区管理系统登录页面以及逻辑实现

    学习vue3和springboot那肯定是少不了写项目的 在各个项目中肯定是离不开登录和注册的事情的 这也是一个项目起步的需求 接下来我们来看看我们所写的项目起步 首先搭建vue3和springboot的项目环境 这些搭建大家自行完成即可
  • 《Centos7——实战-完全备份和增量备份》

    目录 完全备份和增量备份 1 需要开启log bin日志 2 增量和全备脚本 3 计划任务 模拟数据丢失 1 新增数据 2 数据备份 3 删除数据 4 数据的恢复 完全备份和增量备份 一般都三种备份种类 完全备份 差异备份 增量备份 完全备
  • Android中的延时工作队列——WorkQueue教程

    Android中的延时工作队列 WorkQueue教程 工作队列是Android开发中常用的一种机制 它允许我们按照一定的顺序和延时执行任务 在本教程中 我们将介绍如何使用Android的WorkQueue来创建延时工作队列 并提供相应的源
  • 云计算————系统部署

    云计算基础篇 前言 云计算 cloud computing 是基于互联网的相关服务的增加 使用和交付模式 通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源 云是网络 互联网的一种比喻说法 过去在图中往往用云来表示电信网 后来也用来表示
  • hibernate的insert数据正常,但是update数据乱码问题解决

    在做项目是发现 hibernate的insert数据正常 但是update数据是却出现乱码 解决办法 在tomcat的conf server xml中找到下面代码的位置
  • Java JUnit 单元测试小结

    测试类型 单元测试 Unit test 单元测试关注单一的类 它们存在的目的是检查这个类中的代码是否按照期望正确运行 集成测试 Integration test 集成测试是检查开发的模块和其他模块整合时是否正常工作 虽然集成测试的代码影响范