测试用例-单元测试

测试用例-单元测试单元测试 编写手册 1 简述本文主要针对如何使用 Junit 编写单元测试进行描述文中的实例基于 Junit4 所谓单元测试 即是指针对程序中的一些单元进行测试的方法这些单元在 Junit 中的最小单位为方法借助单元测试 我们可以轻松地单独测试程序中的某一个逻辑片段而不需要在意程序的外部依赖和其它逻辑接口测试单元测试只能以接口为维度进行测试只需被测试的单元逻辑正常即可工程必须编译通过并打包进行部署可以不依赖外部 测试进度不再受制于外部条件工程的外部依赖 数据库 调用

单元测试——编写手册

1.简述

所谓单元测试,即是指针对程序中的一些单元进行测试的方法 这些单元在Junit中的最小单位为方法 借助单元测试,我们可以轻松地单独测试程序中的某一个逻辑片段而不需要在意程序的外部依赖和其它逻辑 
接口测试 单元测试
只能以接口为维度进行测试 只需被测试的单元逻辑正常即可
工程必须编译通过并打包进行部署 可以不依赖外部,测试进度不再受制于外部条件
工程的外部依赖(数据库、调用的服务等)必须就绪 可以以方法为维度进行测试
难以测试复杂的逻辑分支,为测试数据需要调整各个数据源(数据库、缓存、消息队列) 可以根据单元的逻辑复杂程度编排测试用例数量,测试使用的数据可以自由调整

2.创建测试用例

2.1工程准备

  1. 确保工程的maven依赖中包含junit,版本至少为4.12,一般包含在spring-boot-starter-test中;
  2. 确保工程的目录中包含src/test/java和src/test/resource;

2.2 编写测试启动类

许多工程在启动时需要加载许多配置类,与外部系统进行连接,非常复杂 为了使单元测试更加轻便,我们可以编写单元测试专用的启动类,屏蔽一些不相关的启动项 若你的工程启动和外部连接依赖本身就很简单,可以省略这一步,不写测试启动类则执行测试时默认使用 src/main/java下的启动类 
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; @ComponentScan(basePackages = { 
    "需要扫描的包名" }, excludeFilters = { 
    @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = { 
    扫描的包中需要排除的类 }) }) @SpringBootApplication(exclude = { 
    需要排除的一些启动时自动配置类 }) public class TestApplication { 
    public static void main(String[] args) { 
    SpringApplication.run(TestApplication.class, args); } } 

这个启动类中常用的配置如下

  1. @ComponentScan
    用于扫描指定的包,excludeFilters用于排除扫描的包中不需要的Bean
  2. @SpringBootApplication
    用于排除一起自动配置的启动类,防止诸如数据库、Mongo等启动类进行外部连接
    测试的启动类与工程的启动类类似,它通常防止在src/test/java下的项目根包中

其中“需要扫描的包名”和“扫描的包中需要排除的类”根据具体的工程决定,可按需将工程中的一些启动时配置类排除在外(Cache配置等),保证你测试的类以及它们的直接依赖被扫描到即可
“需要排除的一些启动时自动配置类”可参考如下对照表按需排除

作用 全类名
MongoDB自动配置 org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration
数据源自动配置 org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration
Hibernate注解自动配置 org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration

启动类的扫描配置决定了测试用例的范围以及启动速度
可以按需编写多个不同扫描范围的启动类以适配不同的测试需求

2.3 构建抽象测试类

根据启动类创建一个抽象测试类是个好方法,它能够让你在创建测试用例时通过选择继承的父类直接确定使用的测试启动配置。

import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = WebEnvironment.NONE, classes = 启动类) public abstract class BaseTest { 
    } 

2.4 使用测试专用的配置

2.5 新建Test类

针对一个被测类创建一个测试类 测试类通常以被测类的名字+Test进行命名,并放置在与src/test/java下与被测类同包名的包中 
  1. 根据被测类创建测试类
  2. 配置测试类的参数,通常这些参数会按照约定给出一些默认值;
  3. 选择要测试的方法,注意:junit测试只能对非private方法进行测试DemoServiceTest,测试用例在src/test/java下的被测类同名包中;此时可以选择继承的父类,根据测试需要进行继承
  4. 生成测试类,得到一个单元测试类的骨架,包含了选择的方法的测试用例

编写用例

3.1 了解Mock测试

在这里插入图片描述
一个Bean通常会形成一个依赖树,这种发散的依赖结构导致我们在测试一个类的逻辑时其实是连同它所依赖的逻辑一同进行测试

此时使用Mock测试就可以解决依赖过多,逻辑复杂的问题

Mock以为模拟、虚拟,就是将原有的逻辑进行模拟,使用规划的Mock方案逻辑进行替代

在Mock测试中,我们通常对测试对象的所有直接依赖进行Mock,被Mock的直接依赖将变成只有方法签名的空壳

被调用时它们不会再调用间接依赖,也不会执行原有的逻辑,只会根据Mock方案进行返回

这样在测试测试对象时我们就不在需要关心它负载的间接依赖关系和所有依赖的内部逻辑了,只需要专注于当前测试对象的逻辑即可

3.2 完全Mock依赖

  1. @MockBean标记的依赖的方法默认变为空方法且返回值为null,一定要配合mock方案才能执行逻辑和获取返回值(参照FAQ-@MockBean与@SpyBean)
  2. mock方案的编写参照FAQ-mock方案
  3. 需要调用@MockBean标记的类中无返回值的方法时可以不编写mock方案,但一定要校验调用是否执行(参照FAQ-验证调用是否执行)

3.3 完全真实依赖

这种类型的测试同时测试测试对象及全部其依赖的逻辑,针对实际调用流程进行测试 适用于测试一个完整的流程以及测试调用链路正确性 
  1. 这种测试方式与Mock毫无关系,为常规的方法逻辑调用
  2. 测试的结果与输入的数据、持久化的数据、程序逻辑直接相关,因此需要事先准备好配套得入参、数据库脚本/Redis缓存并熟悉程序的逻辑,以此推导出正确的处理结果

3.4 Mock与真实依赖相结合

这种类型的测试同时测试测试对象及其部分依赖的逻辑,屏蔽部分较为复杂的依赖同时对实际调用流程进行测试 适用于测试一个部分依赖较为复杂的流程以及测试调用链路正确性 
  1. @SpyBean标记的依赖的方法默认为真实逻辑,若配合mock方案则在入参与mock方案一致时根据mock方案的设置进行返回(参照FAQ-@MockBean与@SpyBean)
  2. mock方案的编写参照FAQ-mock方案
  3. 需要调用@SpyBean标记的类的方法的真实逻辑时可以不编写mock方案
  4. 测试的结果与输入的数据、持久化的数据、程序逻辑、mock方案均相关,因此需要事先准备好配套得入参、数据库脚本/Redis缓存并熟悉程序的逻辑和mock方案的逻辑,以此推导出正确的处理结果
  5. 虽然@SpyBean标记的类在没有设置mock方案的情况下原则上与原类的逻辑一致,但由于底层实现原理的种种限制,它与原生的类在某些情况下不能完全一致;若想直接测试完全真实依赖的场景,请参照完全使用真实依赖的测试章节的描述

3.5 混合测试

对一个测试对象中的不同方法采取不同的测试策略 对同一个测试对象的不同方法根据需求采取“完全Mock”或“完全真实”或“Mock与真实结合”的方式;没有对同一个测试 对象编写多个不同类型测试用例的需求 

注意

  1. 此种测试是其他3种的复合形式,单个用例内的书写参照相应章节的描述
  2. 此种测试的测试对象的依赖需要使用@SpyBean的方式进行标记,否则难以执行真实逻辑(参照FAQ-@MockBean与@SpyBean)

4. FAQ

4.1 @MockBean与@SpyBean

在进行mock测试或混合测试时可以看到这2中不同的设定测试对象直接依赖的注解 
/ 相同 不同
@MockBean 提供mock测试能力mock方案的作用域都在一个@Test内 默认将标记该注解的类及其依赖的方法全部掏空,直接调用将没有任何执行逻辑并返回null(如果有返回值)
@SpyBean 提供mock测试能力mock方案的作用域都在一个@Test内 默认将标记该注解的类以及依赖的方法保持原样,直接调用将按照方法原本的逻辑执行并返回;可以按照混合测试的需求灵活决定是否mock
/ 使用场景
@MockBean 适用于标记的依赖需要被完全mock的场景,不配合mock方案使用易引发空指针异常
@SpyBean 适用于标记的依赖只有部分逻辑需要mock的场景,不配合mock方案则执行原方法,配合mock方案则执行mock方案

4.2 mock方案

Mockito.doReturn(mockData).when(demoService).function(Matchers.eq(arg1), Matchers.eq(arg2)); Mockito.doNothing().when(demoService).function(Matchers.eq(arg1), Matchers.eq(arg2)); Mockito.doThrow(exception).when(demoService).function((Matchers.eq(arg1), Matchers.eq(arg2)); 
关键词 解释
doReturn(mockData) 模拟方法正常返回数据
doNothing() 模拟方法未执行(可用于没有返回值的方法)
doThrow(exception) 模拟方法执行时抛出异常
demoService 被@MockBean或@SpyBean标记的测试对象的直接依赖
function、arg1、arg2 直接依赖的方法名及其参数
Matchers.eq() org.mockito.Matchers提供的一些更灵活的mock调用时参数验证方法;验证通过时方法返回模拟的返回数据,验证不通过时方法按照@MockBean或@SpyBean的默认策略执行

4.3 同名方法调用多次

@MockBean private DemoService demoService; @Test public void testFunction() { 
    // 省略其他步骤,此处只展示mock方案设置 // 方案1 Mockito.doReturn(demoDataA).when(demoService).callFun(eq("alpha")); // 方案2 Mockito.doReturn(demoDataB).when(demoService).callFun(eq("beta")); // 方案3 Mockito.doCallRealMethod().when(demoService).callFun(eq("beta")); } 
@MockBean private DemoService demoService; @Test public void testFunction() { 
    // 省略其他步骤,此处只展示mock方案设置 // 方案1 Mockito.doReturn(demoDataA).when(demoService).callFun(eq("alpha")); // 方案2(包含多次调用的方案) Mockito.doReturn(demoDataB).doCallRealMethod().when(demoService).callFun(eq("beta")); } 

调用demoService.callFun(String)方法时:

4.4 验证调用是否执行

  1. 测试的依赖使用@SpyBean标注
  2. 执行到mock方案对应的方法,入参与mock方案预期不一致
  3. 方法按照真实逻辑执行
  4. 方法的返回值恰巧与预期一致
  5. 测试成功
Mockito.verify(demoService).function(Matchers.eq(arg1), Matchers.eq(arg2)); 
Mockito.verify(demoService, new Times(2)).function(Matchers.eq(arg1), Matchers.eq(arg2)); 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/233076.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 如何下载mysql驱动jar包

    如何下载mysql驱动jar包mysql的驱动jar包下载教程

    2022年5月11日
    39
  • Mybatis分页插件-PageHelper的使用

    Mybatis分页插件-PageHelper的使用Mybatis分页插件-PageHelper的使用怎样配置mybatis这里就不提了,我来说说我配置这个分页插件的过程吧。下载JAR包分页插件pagehelper.jar:https://oss.sonatype.org/content/repositories/releases/com/github/pagehelper/pagehelper/http://repo1.maven.org/ma

    2022年5月22日
    39
  • UBUNTU12.04系统安装完成后的必要准备

    UBUNTU12.04系统安装完成后的必要准备

    2021年8月21日
    227
  • C++代码算法题:(5).最长回文子串

    C++代码算法题:(5).最长回文子串题目及要求:给你一个字符串s,找到s中最长的回文子串。提示:1<=s.length<=1000s仅由数字和英文字母(大写和/或小写)组成原创代码:classSolution{public:stringlongestPalindrome(strings){intbegin=0;//每个当前子串的开头intend=0;//每个当前子串的末尾intvalue=0;//判断条件使用。条

    2022年6月10日
    31
  • Mac系统Unity3D中的快捷键

    Mac系统Unity3D中的快捷键command+N新建场景command+O打开场景command+S保存场景Shift+command+S场景另存为Shift+command+B编译设置command+B编译并运行command+zUndo撤销shift+command+zRedo撤销command+XCut剪切command

    2022年5月22日
    57
  • .gho文件检查

    .gho文件检查虽然目前windows10的接受程度越来越广泛,但我接触到的一些非IT人士还是钟爱于windows7系统,本文记录一下在使用ghost还原系统遇到的问题。gho还原失败在还原ghost系统过程中,遇到gho文件损坏,还原失败,导致系统重装卡住,且大部分的PE系统都不能上网,这就比较麻烦了。因为gho文件一直保存于U盘中,可能在平时使用U盘过程中操作不当导致数据出现损坏。为了避免这种事情的再次发生,…

    2022年7月14日
    14

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号