如何进行单元测试

news2024/12/18 19:19:31

  1. 前言

单元测试是指对软件中最小可测单元进行检查和验证;c语言中单元指一个函数,java中指一个类。图形化软件中可以指一个窗口或者一个菜单。总的来说,单元就是认为规定最小的被测试模块。

1.1单元测试对我们开发程序有什么好处

首先是一个前端单元测试的根本性原由:JavaScript 是动态语言,缺少类型检查,编译期间无法定位到错误; JavaScript 宿主的兼容性问题。比如 DOM 操作在不同浏览器上的表现。

正确性:测试可以验证代码的正确性,在上线前做到心里有底。

自动化:当然手工也可以测试,通过console可以打印出内部信息,但是这是一次性的事情,下次测试还需要从头来过,效率不能得到保证。通过编写测试用例,可以做到一次编写,多次运行。

解释性:测试用例用于测试接口、模块的重要性,那么在测试用例中就会涉及如何使用这些API。其他开发人员如果要使用这些API,那阅读测试用例是一种很好地途径,有时比文档说明更清晰。

驱动开发,指导设计:代码被测试的前提是代码本身的可测试性,那么要保证代码的可测试性,就需要在开发中注意API的设计,TDD将测试前移就是起到这么一个作用。

保证重构:互联网行业产品迭代速度很快,迭代后必然存在代码重构的过程,那怎么才能保证重构后代码的质量呢?有测试用例做后盾,就可以大胆的进行重构。

  1. 规范

2.1 单元测试主体

大多数单元测试包括四个主体:

  • 测试套件describe、

  • 测试用例it、

  • 判定条件expect、

  • 断言结果toEqual。

2.2 单元测试用例的原则

  • 测试代码时,只考虑测试,不考虑内部实现;

  • 数据尽量模拟现实,越靠近现实越好,

  • 充分考虑数据的边界条件下·

  • 对重点、复杂、核心代码、重点测试

  • 利用AOP(面向切面编程),减少测试代码,避免无用功能

  • 测试、功能开发相结合,有利于设计和代码重构

  1. 常用框架

3.1 Spock

Spock主要特点如下:

  • 让测试代码更规范,内置多种标签来规范单元测试代码的语义,测试代码结构清晰,更具可读性,降低后期维护难度。

  • 提供多种标签,比如:givenwhenthenexpectwherewiththrown……帮助我们应对复杂的测试场景。

  • 使用Groovy这种动态语言来编写测试代码,可以让我们编写的测试代码更简洁,适合敏捷开发,提高编写单元测试代码的效率。

  • 遵从BDD(行为驱动开发)模式,有助于提升代码的质量。

  • IDE兼容性好,自带Mock功能。

3.1.1 为什么使用Spock? Spock和JUnit、jMock、Mockito的区别在哪里?

总的来说,JUnit、jMock、Mockito都是相对独立的工具,只是针对不同的业务场景提供特定的解决方案。其中JUnit单纯用于测试,并不提供Mock功能。

我们的服务大部分是分布式微服务架构。服务与服务之间通常都是通过接口的方式进行交互。即使在同一个服务内也会分为多个模块,业务功能需要依赖下游接口的返回数据,才能继续后面的处理流程。这里的下游不限于接口,还包括中间件数据存储比如Squirrel、DB、MCC配置中心等等,所以如果想要测试自己的代码逻辑,就必须把这些依赖项Mock掉。因为如果下游接口不稳定可能会影响我们代码的测试结果,让下游接口返回指定的结果集(事先准备好的数据),这样才能验证我们的代码是否正确,是否符合逻辑结果的预期。

尽管jMock、Mockito提供了Mock功能,可以把接口等依赖屏蔽掉,但不能对静态方法Mock。虽然PowerMock、jMockit能够提供静态方法的Mock,但它们之间也需要配合(JUnit + Mockito PowerMock)使用,并且语法上比较繁琐。工具多了就会导致不同的人写出的单元测试代码“五花八门”,风格相差较大。

Spock通过提供规范性的描述,定义多种标签(givenwhenthenwhere等),去描述代码“应该做什么”,“输入条件是什么”,“输出是否符合预期”,从语义层面规范了代码的编写。

Spock自带Mock功能,使用简单方便(也支持扩展其他Mock框架,比如PowerMock),再加上Groovy动态语言的强大语法,能写出简洁高效的测试代码,同时能方便直观地验证业务代码的行为流转,增强工程师对代码执行逻辑的可控性。

3.1.2使用Spock解决单元测试开发中的痛点

如果在(if/else)分支很多的复杂场景下,编写单元测试代码的成本会变得非常高,正常的业务代码可能只有几十行,但为了测试这个功能覆盖大部分的分支场景,编写的测试代码可能远不止几十行。

之前有遇到过某个功能上线很久一直都很正常,没有出现过问题,但后来有个调用请求的数据不一样,走到了代码中一个不常用的逻辑分支时,出现了Bug。当时写这段代码的同学也认为只有很小几率才能走到这个分支,尽管当时写了单元测试,但因为时间比较紧张,分支又多,就漏掉了这个分支的测试。

尽管使用JUnit的@Parametered参数化注解或者DataProvider方式可以解决多数据分支问题,但不够直观,而且如果其中某一次分支测试Case出错了,它的报错信息也不够详尽。

这就需要一种编写测试用例高效、可读性强、占用工时少、维护成本低的测试框架。首先不能让业务人员排斥编写单元测试,更不能让工程师觉得写单元测试是在浪费时间。而且使用JUnit做测试工作量不算小。据初步统计,采用JUnit的话,它的测试代码行和业务代码行能到3:1。如果采用Spock作为测试框架的话,它的比例可缩减到1:1,能够大大提高编写测试用例的效率。

3.2 Mockito

3.2.1什么是 Mockito

Mockito 是一个强大的用于 Java 开发的模拟测试框架, 通过 Mockito 我们可以创建和配置 Mock 对象, 进而简化有外部依赖的类的测试.

使用 Mockito 的大致流程如下:

  • 创建外部依赖的 Mock 对象, 然后将此 Mock 对象注入到测试类中.

  • 执行测试代码.

  • 校验测试代码是否执行正确

3.2.2为什么使用 Mockito

假设我们正在编写一个银行的服务 BankService, 这个服务的依赖关系如下:

当我们需要测试 BankService 服务时, 该真么办呢?

一种方法是构建真实的 BankDao, DB, AccountService 和 AuthService 实例, 然后注入到 BankService 中.

不用我说, 读者们也肯定明白, 这是一种既笨重又繁琐的方法, 完全不符合单元测试的精神. 那么还有一种更加优雅的方法吗?

自然是有的, 那就是我们今天的主角 Mock Object. 下面来看一下使用 Mock 对象后的框架图:

我们看到, BankDao, AccountService 和 AuthService 都被我们使用了虚拟的对象(Mock 对象) 来替换了, 因此我们就可以对 BankService 进行测试, 而不需要关注它的复杂的依赖了.

3.3 JUnit5

3.3.1 JUnit 5架构体系

作为最新版本的JUnit框架,JUnit 5相比之前版本的JUnit框架有了较大的突破,添加了许多新特性。

之前的JUnit 框架所有的功能都被打包在一个构件(artifact)中。它被提供给开发者、IDE、构建工具、其他测试框架、其他扩展等使用,不同的使用者,依赖的都是一个同样的jar包。

而JUnit 5 最重要的一大优化就是其架构体系,它由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

JUnit Platform 其主要作用是在 JVM 上启动测试框架。它定义了一个抽象的 TestEngine API 来定义运行在平台上的测试框架,同时还支持通过命令行、Gradle 和 Maven 来运行平台。通过 JUnit Platform,其他的自动化测试引擎或开发人员自己定制的引擎都可以接入 Junit 实现对接和执行

JUnit Jupiter 包含了 JUnit 5 最新的编程模型和扩展机制; Jupiter 本身也是一个基于 Junit Platform 的引擎实现。

JUnit Vintage 兼容JUnit3,JUnit4 版本的测试引擎

再来看下JUnit 5的架构图

我们来进一步看看各个子项目的作用。

第一层 : 开发人员(这里只进行业务开发撰写单元测试) 使用junit-jupiter-api等测试框架api编写单元测试

第二层 : 测试引擎,JUnit 或其他测试框架实现引擎API的框架,jupiter-engine和 vintage-engine分别是junit 4和junit 5 对测试引擎API的实现,其他的测试框架也可以通过实现引擎API从而接入JUnit 平台

第三层: 平台引擎 junit-platform-engine 是上一层各种引擎实现的抽象,即引擎的接口标准。

第四层: 启动器 通过ServiceLoader发现测试引擎的实现并安排其执行。 它为IDE和构建工具提供了API,因此IDE可以与测试执行交互,例如,通过启动单个测试并显示其结果。

3.3.2 JUnit 5的新特性

  • 包可见性

在JUnit 4里我们的测试方法必须定义为public的访问级别,如果没有定义成public,虽然编译的时候不会提示异常,但是在运行时会提示 “java.lang.Exception: Method testIsBlank() should be public” 如下错误信息。

而在JUnit 5里,我们不再需要将测试类与测试方法定义为public了,默认的包可见的访问级别就可以了。

  • 常用注解

对于在JUnit 4中的常用注解,你都可以在JUnit 5中找到对应的注解,关系如下:

JUnit 5JUnit 4说明

@Test@Test指明被注解的方法是一个测试方法,注意JUnit 5的@Test在jupiter-api里

@BeforeAll@BeforeClass被注解的静态方法会在当前类的所有@Test方法执行前执行一次

@BeforeEach@Before被注解的方法会在当前类的每个@Test方法执行前执行一次

@AfterAll@AfterClass被注解的静态方法会在当前类的所有@Test方法执行后执行一次

@AfterEach@After被注解的方法会在当前类的每个@Test方法执行后执行一次

@Disabled@Ignore被注解的方法不会被执行,但是在测试报告里会记录为已执行

  • 测试命名

写测试用例的时候,为了更好的可读性,我们往往会给测试方法定义一个有意义的名字,eg.testXXXWhenXXXThenReturnXXX。JUnit 5还提供了一个@DisplayName 注解,方便我们为每个测试用例添加更具体的名字,更容易表述用例所要测试的内容(可以是字符串,特殊符号,甚至是表情符号)。

  • 断言

junit 框架中最常用的断言就是检查一个对象或者属性是否为null.或者判断两个属性是否一致。JUnit 4和JUnit 5中的断言方法都可以接受字符串作为一个可选参数,如果断言失败,则会在控制台输出对应的描述信息。JUnit 5中还可以使用 lambda 表达式 来构建这个描述信息。

注意一下,在JUnit 4和JUnit 5中描述信息的参数位置是不一样的。

  • tag标记

JUnit 5新增了@Tag注解,可以为测试类或方法添加标签,并在执行时快速地根据标签来针对性地运行测试。

  • 扩展机制

在JUnit 5出来之前,我们如果想对JUnit 4 的核心功能进行扩展,往往都会使用自定义Runner 和 @Rule。

自定义Runner 通常是 BlockJUnit4ClassRunner 的子类,用于实现 JUnit 中没有直接提供的某种功能。 eg.spring-test框架的SpringJUnit4ClassRunner, 和mock框架的MockitoJUnitRunner

局限性:

必须在测试类级别上使用 @RunWith 注解来声明 Runner 。

@RunWith仅接受一个参数: Runner 的实现类。因为每个测试类最多只能拥有一个 Runner ,所以每个测试类最多也只能拥有一个扩展点。(在PowerMock中引入了@PowerMockRunnerDelegate,可以同时使用两个Runner)

为了解决 Runner 的限制,JUnit 4.7 引入了 @Rule 。一个测试类可声明多个 @Rule ,这些规则可在类级别和测试方法级别上运行,但是它只能在测试运行之前或之后执行指定操作。如果我们想在此之外的时间点进行扩展,@Rule也无法满足我们的要求。

JUnit 5扩展机制的核心准则:

Prefer extension points over features

基于这一准则,JUnit 5 中定义了许多扩展点,每个扩展点都对应一个接口。我们可以定义自己的扩展可以实现其中的某些接口,然后通过 @ExtendWith 注解注册给 JUnit,后者会在特定的时间点调用注册的接口实现。

参考:

Spock单元测试框架介绍以及在美团优选的实践

学习单元测试 Mockito

Mockito详细教程

关于JUnit5 你必须知道的(一)

关于JUnit5 你必须知道的(二)

关于JUnit5 你必须知道的(三)

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

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

相关文章

react: input 输入框 中文onChange事件异常问题 对input输入进行防抖处理

当我们使用Input时,我们可能会遇到一个问题,比如需要对用户输入的内容进行搜索时,当用户处于中文输入时,明明没有对内容进行确认,为什么会触发了onChange事件呢?比如以下场景,中文一边输入另外一…

机器学习知识总结 —— 20.使用朴素贝叶斯进行数据分类

文章目录准备基础数据计算先验概率计算条件概率预测分布验证结果作为一种监督学习分类方法,在上一章中我们已经介绍过它的数理原理。现在我们开始来实现一个简单的朴素贝叶斯分类的算法,这样我们能更好的理解它是怎么运作的。 准备基础数据 首先还是有…

加密流量专栏总览

文章目录加密流量专栏1. 原理篇2. 模型篇3. 文章分类总结3.1 研究方向3.2 特征提取3.3 机器学习模型改进3.4 深度学习模型改进3.5 其他模型改进3.7 实时检测3.8 概念漂移检索论文的方法加密流量专栏 1. 原理篇 原理: 会话、流、数据包之间的关系。 流:…

【离线数仓-4-数据仓库设计-分层规划构建流程】

离线数仓-4-数据仓库设计-分层规划&构建流程离线数仓-4-数据仓库设计-分层规划&构建流程1.数据仓库分层规划2.数据仓库构建流程1.数据调研1.业务调研2.需求分析3.总结2.明确数据域3.构建业务总线矩阵&维度模型设计4.明确统计指标1.指标体系相关概念1.原子指标2.派生…

【渝偲医药】DSPE-PEG-RGD;磷脂聚乙二醇多肽试剂级简介

DSPE-PEG-RGD、 二硬脂酰基磷脂酰乙醇胺-聚乙二醇-多肽、磷脂PEG多肽 英文名称: 1,2-Distearoyl-sn-Glycero-3-Phosphoethanolamine-PEG- RGD 溶剂:可溶解在水中和大多数有机溶剂中 外观:白色粉末 用途:用于链接带有链霉亲和素或其他的基团的分子 分子量(PEG ):2000、3400、…

那些开发过程中需要遵守的开发规范

入职公司三天,没干啥其他活,基本在配置本地环境和阅读相关文档。技术方面公司基本用的是主流的技术体系,入职后需要先阅读阿里的开发规范和其他的一些产研文档。今天整理一些平时需要关注的阿里规约和数据库开发规范,方便今后在开…

TatukGIS Developer Kernel for .NET

TatukGIS Developer Kernel for .NET 用于.NET的TatukGIS开发人员内核的强大功能: 打开、创建、编辑、保存和导出矢量、图片和网格的过程,包括类似于数据库的格式。 扩展属性、北箭头、比例和其他视觉控制也从TatukGIS编辑器/查看器商品中显示给用户开发…

Java基础系列(五): final关键字用法

一. 概述 final关键字代表最终,不可改变的. 常见有5种用法,我们来归纳总结一下: 1. 用来修饰一个类 2. 用来修饰一个方法 3. 用来修饰成员变量 4. 用来修饰局部变量 5. 用来修饰方法参数 二. final饰修类 如果声明一个类为final类, 那么这个类就是最终类,不能被继承 …

7 Python文件、文件夹、word及excel操作

0 建议学时和要求 4学时 掌握os和os.path模块对文件和文件夹操作的函数 掌握shutil模块对文件和文件夹操作的函数 掌握扩展库openpyxl对Excel文件的操作 1 文件的高级操作 1.1 文件的概念及分类 文本文件 文本文件可以使用记事本、gedit、ultraedit等字处理软件直接进行显…

ESP32设备驱动-DS1264数字温度传感器驱动

DS1264数字温度传感器驱动 1、DS1264介绍 DS1624 由两个独立的功能单元组成:一个 256 字节非易失性 E2 存储器和一个直接数字温度传感器。 非易失性存储器由 256 字节的 E2 存储器组成。 该存储器可用于存储用户希望的任何类型的信息。 这些内存位置通过 2 线串行总线访问。…

007永磁电机控制方式:别张嘴就FOC,其他常规控制方式也是伺服人的基本功

在读本篇文,我想做个小调查。到目前为止,你掌握的或者是你了解到的控制永磁同步电机的方式都有哪些?我想,你大概张口就说FOC控制吧。没错,FOC控制是我们日常生活中所见到的最普遍的永磁同步电机的控制方式。当然在本专…

微信电脑版字体模糊(或文字太小)怎么调整

文章目录第一步:设置屏幕缩放125%第二步:文本大小设置为125%第三步:微信设置--通用--勾选“适配系统缩放比例”第四步:微信高DPI缩放行为设置(关键)ClearType勾选(可选)笔者遇到这个…

【模板】线段树 2

题目描述 如题,已知一个数列,你需要进行下面三种操作: 将某区间每一个数乘上 xxx 将某区间每一个数加上 xxx 求出某区间每一个数的和 输入格式 第一行包含三个整数 n,m,pn,m,pn,m,p,分别表示该数列数字的个数、操作的总个数…

计算机网络笔记(复试准备)第一章

计算机网络笔记(复试准备) 第一章 网络,互联网与因特网 网络由若干个结点和连接这些结点的链路组成 多个网络通过路由器连接起来这也就形成了一个更大的网络即是我们熟知的互联网也就是“网络的网络” 因特网是世界上最大的网络 问&#xf…

Open-Vocabulary Object Detection Using Captions论文讲解

文章目录一、论文前言二、提出原因三、论文的核心四、论文讲解4.1 论文流程4.2 OVD与之前相关的setting4.3 结果对比一、论文前言 目标检测是人工智能最突出的应用之一,也是深度学习最成功的任务之一。 然而,尽管深度对象检测取得了巨大进步&#xff0…

MongoDB在银行海量历史订单交易数据查询中的应用(Spring boot + Bee)

MongoDB在银行海量历史订单交易数据查询中的应用(Spring boot Bee) 近年来,随着各种便捷支付方式的普及,银行账户交易数据呈现爆炸式增长,同时数据模型也在不断变化,传统关系型数据库已难以满足这种海量的、模式灵活、高可用、高性能的数据存储和查询需求。通过对银行历史交易…

【编程入门】应用市场(php版)

背景 前面已输出多个系列: 《十余种编程语言做个计算器》 《十余种编程语言写2048小游戏》 《17种编程语言10种排序算法》 《十余种编程语言写博客系统》 《十余种编程语言写云笔记》 《N种编程语言做个记事本》 目标 为编程初学者打造入门学习项目,使…

【MySQL】索引常见面试题

文章目录索引常见面试题什么是索引索引的分类什么时候需要 / 不需要创建索引?有什么优化索引的方法?从数据页的角度看B 树InnoDB是如何存储数据的?B 树是如何进行查询的?为什么MySQL采用B 树作为索引?怎样的索引的数…

033_SS_Inversion-Based Creativity Transfer with Diffusion Models

下载地址:Arxiv 2022.11.23 Code地址:https://github.com/zyxElsa/creativity-transfer 1. Introduction Motivations 以前的任意示例引导的艺术图像生成方法(比如风格迁移)通常无法控制形状变化或传达语义元素。而预训练的text…

【Linux | ELK 8.2】搭建ELKB集群Ⅰ—— 实验环境说明和搭建Elasticsearch集群

目录1. 实验环境1.1 实验工具1.2 操作系统1.3 架构版本、IP地址规划与虚拟机配置要求1.4 拓扑图1.5 其他要求2. 实验步骤2.1 安装Elasticsearch(单节点)(1)检查系统jdk版本(2)下载elasticsearch&#xff08…