Mockito记录和静态方法的模拟-程序员宅基地

技术标签: Java  junit  单元测试  testNG  

单元测试那些坑

1. 前言

为了提高白盒测试覆盖率,项目中需要添加单元测试代码,写单元测试中很多都是用的Mock+Junit,但是我这个项目中使用的是Mock+testng,不过这两种方式我都会介绍。

2. Mock+TestNG单元测试

2.1 前提准备

这里提供一份依赖jar包的pom文件:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.10.19</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.0.0</version>
    <scope>test</scope>
</dependency>
<!--静态类方法模拟-->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-testng</artifactId>
    <version>1.7.4</version>
    <scope>test</scope>
</dependency>

2.2 接口方法测试

在接口中我们肯定会有一些自动注入(@Autowired)的Dao层对象或者其他对象,在Mock中有两种方式表示这些自动注入的对象

2.2.1 使用注解自动初始化

自动化注解需要使用@InjectMocks@Mock搭配使用,这样就可以在测试类运行的时候Mock这些自动注入对象,之后在@BeforeTest 中使用MockitoAnnotations.initMocks(this); 就可以了。

实例代码:

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
public class IndexServiceImplTest {
    
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @BeforeTest
    public void beforeTest() {
    
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
    
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}
2.2.2 使用反射机制初始化注入对象

这种是很简便的使用,如果想多写几行代码可以使用反射机制,set这些对象。

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.lang.reflect.Field;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
public class IndexServiceImplTest {
    
    IndexServiceImpl service = new IndexServiceImpl();
    
    @Mock
    private FirstRepository firstRepository;
    
    @BeforeTest
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
    
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        MockitoAnnotations.initMocks(this);
        
        Class serviceClass = service.getClass();
        // 反射获取属性
        Field firstRepositoryField = serviceClass.getDeclaredField("firstRepository");
        firstRepositoryField.setAccessible(true);
        firstRepositoryField.set(service, firstRepository);
    }
    
    @Test
    public void getPerson() {
    
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

2.3 静态类方法测试

在复杂的接口业务中,经常会用到一些工具类,静态方法等等,这类单元测试需要通过PowerMockito来实现静态方法的模拟。

静态方法:

public class StaticUtils {
    
    
    public static String printList(List<String> list) {
    
            System.out.println("============ StaticUtils.printHello()============");
            list.forEach(System.out::println);
            return "printList";
        }
}

接口类添加静态方法:

@Override
    public ResponseEntity<Object> getPerson(String userId, String userName) {
    
        String s = StaticUtils.printList(Arrays.asList("a", "b", "c"));
        FirstEntity firstEntity = firstRepository.findFirstByUserIdAndAndUserName(userId,userName);
        return ResponseEntity.ok().body(firstEntity);
    }

PowerMockito.mockStatic(StaticUtils.class); 要放在 MockitoAnnotations.initMocks(this); 前面才可以,示例代码:

import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;
import com.xing.springDataJpa.utils.StaticUtils;

import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockObjectFactory;
import org.springframework.http.ResponseEntity;
import org.testng.Assert;
import org.testng.IObjectFactory;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.ObjectFactory;
import org.testng.annotations.Test;

/**
 * 功能描述
 *
 * @since 2020-09-28
 */
@PrepareForTest(StaticUtils.class)
public class IndexServiceImplTest  {
    
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @ObjectFactory
    public IObjectFactory getObjectFactory() {
    
        return new PowerMockObjectFactory();
    }
    
    @BeforeTest
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
    
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        PowerMockito.mockStatic(StaticUtils.class);
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
    
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        // 模拟静态方法
        String rel = "rel";
        PowerMockito.when(StaticUtils.printList(anyListOf(String.class)))
                .thenReturn(rel);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

3. Mock+Junit单元测试

完整的代码:

import static org.mockito.Matchers.anyListOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

import com.xing.springDataJpa.first.FirstEntity;
import com.xing.springDataJpa.first.FirstRepository;
import com.xing.springDataJpa.service.impl.IndexServiceImpl;
import com.xing.springDataJpa.utils.StaticUtils;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.http.ResponseEntity;


/**
 * 功能描述
 *
 * @since 2020-09-28
 */

@RunWith(PowerMockRunner.class)
@PrepareForTest(StaticUtils.class)
public class IndexServiceImplTest  {
    
    @Mock
    private FirstRepository firstRepository;

    // @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
    @InjectMocks
    private IndexServiceImpl service;
    
    @Before
    public void beforeTest() throws NoSuchFieldException, IllegalAccessException {
    
        System.out.println("========= beforeTest() ==========");
        // 其中this就是单元测试所在的类,在initMocks函数中Mockito会根据类中不同的注解(如@Mock, @Spy等)创建不同的mock对象,即初始化工作
        PowerMockito.mockStatic(StaticUtils.class);
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void getPerson() {
    
        String userId = "3423423113";
        String userName = "xiaoming";
        FirstEntity firstEntity = new FirstEntity();
        firstEntity.setUserId(userId);
        firstEntity.setUserName(userName);
        when(firstRepository.findFirstByUserIdAndAndUserName(anyString(), anyString()))
                .thenReturn(firstEntity);
        // 模拟静态方法
        String rel = "rel";
        PowerMockito.when(StaticUtils.printList(anyListOf(String.class)))
                .thenReturn(rel);
        ResponseEntity<Object> person = service.getPerson(userId, userName);
        Assert.assertNotNull(person);
    }
    
}

相比较TestNG 只有部分代码不一样其他都差不多。

4. 注解说明

这里是TestNG的一些注解说明:

@BeforeClass---@AfterClass

类实例化前, 被执行, 主要用于设置环境变量等, 与SpringTestContext结合用的时候要注意, 这种情况下@autowire的bean还未实例化

@BeforeTest----@AfterTest

整个测试类开始前, 被执行, 主要用户塞值, 或者进行mock(Object)的初始化, 此方法只会运行一次

@BeforeMethod-----@AfterMethod

每个测试方法执行前, 进行调用, 和@BeforeTest的主要区别在于, 你如果需要每次清空你测试的一些上下文, 那么需要配合@AfterMethod一起使用
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sdut406/article/details/115304272

智能推荐

前端基础——HTML常用标签_html单标签-程序员宅基地

文章浏览阅读1.9k次。学习目标理解: 相对路径三种形式 应用 排版标签 文本格式化标签 图像标签 链接 相对路径,绝对路径的使用 1. HTML常用标签首先 HTML和CSS是两种完全不同的语言,我们学的是结构,就只写HTML标签,认识标签就可以了。 不会再给结构标签指定样式了。HTML标签有很多,这里我们学习最为常用的,后面有些较少用的,可以查下手册就可以。1..._html单标签

Android 缩略图点击弹出大图效果实现-程序员宅基地

文章浏览阅读8.2k次。该项目设计到以下内容:1、自定义标题栏2、Java和JavaScript的互调3、Activity实现仿Dialog样式4、多线程实现考试倒计时5、退出Activity时保存配置信息6、熟悉UI布局下面通过代码一步一步来解析:首先是准备asset中的本地html文件。java代码

最简单最易实现的SE模块(Squeeze-and-Excitation Networks)_se模块是怎么加入到现有的卷积神经网络模型中的-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏53次。最简单最易实现的SE模块Squeeze-and-Excitation NetworksSENet是Squeeze-and-Excitation Networks的简称,拿到了ImageNet2017分类比赛冠军,其效果得到了认可,其提出的SE模块思想简单,易于实现,并且很容易可以加载到现有的网络模型框架中。SENet主要是学习了channel之间的相关性,筛选出了针对通道的注意力,稍微增加了一..._se模块是怎么加入到现有的卷积神经网络模型中的

微信支付商户平台-配置密钥/API安全教程_商户安全密钥serialno-程序员宅基地

文章浏览阅读2.8k次。设置密匙时一定要按照下图箭头所示的规定设置。32个字符,包含英文和数字,英文要同时有大写和小写。如:bianchengxiaoshitou2501902696AAA。我们通常第一次设置时,需要安装一个证书,证书也是傻瓜式的安装,安装提示一步步来就可以了。我们设置微信支付密匙,关心的是账户中心页面。现在看到的是我写这篇文章当天的后台界面。先我们要去注册微信支付的账号,注册成功后登录微信支付的后台。我们找到API密匙,在这里设置密匙。到这里就成功的设置了微信支付的密匙了。设置完点确定,然后会弹出下面弹窗,_商户安全密钥serialno

vsftpd服务器搭建_ftp主从搭建-程序员宅基地

文章浏览阅读726次。vsftpd服务器搭建1.我是在centos7.6上搭建的2.观看此文章可以帮你巩固 2.1:linux命令 2.2:防火强命令 2.3:vsftpd命令 2.4:设置用户的命令 2.5:查看日志_ftp主从搭建

中国汽车高级驾驶辅助系统(ADAS)行业十四五规划及投资动态分析报告2022-2028年版-程序员宅基地

文章浏览阅读1.6k次。中国汽车高级驾驶辅助系统(ADAS)行业十四五规划及投资动态分析报告2022-2028年版m++m++m++m++m++m++m++m++m++mm++m++m++m++m++m++m++m++m++mm++m+++mm++m+第一章 汽车高级驾驶辅助系统(ADAS)的基本概述第二章 2019-2021年国际汽车高级驾驶辅助系统(ADAS)行业分析2.1 2019-2021年国际ADAS行业发展综况2.1.1 系统发展阶段2.1.2 市场规模分析2.1.3 市场渗透率分析2.1...

随便推点

身份证的加密与压缩思路与实现-程序员宅基地

文章浏览阅读2k次。2019独角兽企业重金招聘Python工程师标准>>> ..._身份证号压缩

提高页面性能的方法_css样式如何提升页面性能-程序员宅基地

文章浏览阅读335次。(1) 减少http请求次数:CSS Sprites, JS、CSS源码压缩、图片大小控制合适;网页Gzip,CDN托管,data缓存 ,图片服务器。 (2) 前端模板 JS+数据,减少由于HTML标签导致的带宽浪费,前端用变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数 (3) 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。 (4)_css样式如何提升页面性能

区块链开发技术路线选择的思考(之一)-程序员宅基地

文章浏览阅读3.7w次,点赞29次,收藏30次。现在整个技术社区的注意力主要还是在 Web 和移动开发上面,相关人才供销两旺。不过个别有心人已经开始转向大数据分析、深度学习、VR/AR 这些前景看好的技术。最近几个月区块链非常火,所以也有极少数开发者在关注区块链的开发技术。_区块链开发技术路线

Datawhale-NLP-1赛题理解_npl入门赛-程序员宅基地

文章浏览阅读92次。Datawhale-NLP-1赛题理解赛题理解学习目标功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入赛题理解赛题名称: 0基础入门的新闻文本分类赛题目标:通过经典文本分类问题,入门NLP的预处理、模型构建和模型训练等知识点_npl入门赛

java中Excel导出模板(跨行跨列导出)_java 导出相同的自动跨列-程序员宅基地

文章浏览阅读2.8k次,点赞2次,收藏17次。java中Excel导出模板(跨行跨列导出)笔者昨天有个需求,就是把下面的课时信息页签的内容原样导出:这个地方看似不难,实际后台很复杂,数据的来源也复杂,并不好处理。但是这不是让我纠结的地方。我纠结的地方是,表头的跨行跨列,而且有的列还是动态的。有了技术问题的时候,捋捋思路后,如果还解决不了,那可能是要去百度了,因为毕竟是在工作,不要耽误时间,赶紧解决问题。七拼八凑整理了一套方法(后边..._java 导出相同的自动跨列

施努卡:机器视觉的五大典型架构和应用案例_机械视觉技术运用案例-程序员宅基地

文章浏览阅读1k次。如今,随着工业4.0的到来,机器视觉技术在工业自动化中逐渐起着十分重要的地位,机器视觉技术的不断创新,推动了工业自动化、智慧安防以及人工智能等行业的进步,机器视觉技术的发展为这项技术所能应用的领域也带来了更多发展潜力与机会。_机械视觉技术运用案例

推荐文章

热门文章

相关标签