什么是Mock
1、Mock定义
Mockito是Java单元测试开发框架。在写测试单元时它可以Mock(Mock的中文释义是模拟,所以Mockito从名字上可以看出是要模拟一种场景)。
它可以模拟任何 Spring 管理的 Bean、模拟方法的返回值、模拟抛出异常等,避免为了测试一个方法,却要自行构建整个bean的依赖链。
Mock 测试主要是用来进行开发中一些未完成的接口或者网络断开、数据库连接错误等方法调用。
举个例子:
如下代码所示,list 集合需要从数据库查询出来。但是如果暂时数据库不能用,有需要测试,这个时候就可以进行 Mock 模拟出符合条件的 list 集合进行本地测试,无需连接数据库。
2、为什么使用
在对代码进行单元测试过程中,经常会有以下的情况发生:
3、常用的Mock技术
- PowerMock
- EasyMock
- Mockito
- JMock
目前在 Java 中主流的 Mock 测试工具有 Mockito、JMock、EasyMock 等等,而 SpringBoot 目前内建的是 Mockito 框架。
4、Mokito中文文档
Mokito中文官网:https://github.com/hehonghui/mockito-doc-zh#0
5、集成测试和单元测试区别
- (1)集成测试
测试过程中,会启动整个Spring容器,调用DB 或者 依赖的外部接口等。只不过访问的环境是测试环境。这个过程最大程度还原生产环境过程,但是耗时长。
- (2)单元测试
不启动整个应用,只对单个接口/类进行测试。不调用DB 、外部接口,依赖的服务都Mock掉,只测试代码逻辑。这个过程,测试用例耗时短。
API
1、Mockito的API
- mock:构建一个我们需要的对象;可以mock具体的对象,也可以mock接口
- spy:构建监控对象
- verify:验证某种行为
- when:当执行什么操作的时候,一般配合thenXXX 一起使用。表示执行了一个操作之后产生什么效果
- doReturn:返回什么结果
- doThrow:抛出一个指定异常
- doAnswer:做一个什么相应,需要我们自定义Answer
- times:某个操作执行了多少次
- atLeastOnce:某个操作至少执行一次
- atLeast:某个操作至少执行指定次数
- atMost:某个操作至多执行指定次数
- atMostOnce:某个操作至多执行一次
- doNothing:不做任何处理
- doReturn:返回一个结果
- doThrow:抛出一个指定异常
- doAnswer:指定一个操作,传入Answer
- doCallRealMethod:返回真实业务执行的结果,只能用于监控对象
2、ArgumentMatchers参数匹配
- anyInt:任何int类型的参数,类似的还有anyLong/anyByte等等。
- eq:等于某个值的时候,如果是对象类型的,则看toString方法
- isA:匹配某种类型
- matches:使用正则表达式进行匹配
3、OngoingStubbing返回操作
- thenReturn:指定一个返回的值
- thenThrow:抛出一个指定异常
- then:指定一个操作,需要传入自定义Answer
- thenCallRealMethod:返回真实业务执行的结果,只能用于监控对象
Mockito的使用
1、添加Maven依赖
- Java 环境依赖
- SpringBoot 环境依赖
注意:SpringBoot 默认的 Mock 框架是 Mockito,和 junit 一样,只需要依赖 spring-boot-starter-test 就可以了。
2、@InjectMocks、@Mock使用
- @Mock: 用于代替Mockito.mock创建mock对象,创建一个Mock实例,需要基于JUnit5环境。
- @InjectMocks: 创建一个实例,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
直白的理解就是:
如图,实体类TUserServiceImpl通过注解 @Autowired注入了三个实体:TUserMapper、JdbcTemplate 和 NamedParameterJdbcTemplate。
如果想要测试TUserServiceImpl,那么test中,TUserMapper、JdbcTemplate 和 NamedParameterJdbcTemplate就要用@Mock注解创建Mock实例,而要被测试TUserServiceImpl就要用@InjectMocks注解创建实例,这个时候被@Mock注解创建的TUserMapper、JdbcTemplate 和 NamedParameterJdbcTemplate就被注入到通过 @InjectMocks注解创建的TUserServiceImpl实例中。
如下图所示:
最后的大白话解释 !!!
你要测试哪个类(如TUserServiceImpl),那么就用 @InjectMocks注解;被测试的类中通过 @Autowired注解注入了几个,那么测试类里面就用@Mock注解创建几个实例!
使用Mockito的注解,需要让注解生效,让注解生效的方法有两个:
- 1.给被测类添加 @RunWith(MockitoJUnitRunner.class) 或者 @RunWith(SpringJUnit4ClassRunner.class) 注解
- 2.在初始化方法中使用MockitoAnnotations.openMocks(this)
注意:新版spring-boot-starter-test不再集成junit,而是junit-jupiter,找不到@RunWith注解:
- spring-boot-starter-test 2.5.5 版本只需要在类上加上@SpringBootTest即可,不需要再加@RunWith()注解了。
- spring-boot-starter-test 2.4.x 版本的也没有@RunWith()注解,至于从哪个版本开始没有@RunWith()注解的,请自行查阅相关文档。
- 一些较低版本也没有 openMocks 方法,而是 initMocks。
3、SpringbootTest 注解和 RunWith 注解在测试类的作用
- @SpringbootTest
这个注解相当于启动类的作用,加了这个注解后,当使用加了@Test注解的方法时,会加载Spring上下文,跟SpringbootApplication这个启动类一样,把bean加载进IOC容器。
其中参数classes 需指明启动类.class,如果不指明,需要保证启动类所在的包和加了SpringbootTest注解的类 在同一个包或者是启动类的子包下,否则注入到( @Autowired / @Resource)会报空指针异常。
如下:
- @RunWith
@RunWith(SpringRunner.class),作用是与Spring环境整合,因为在测试类中我们可以需要用@Autowired自动装配IOC容器中的bean,所以需要与Spring环境进行整合,才能实现自动装配,否则会装配失败,导致bean为null。
有时候会发现,有的测试类不添加@RunWith也能注入成功,这是因为,如果导入@Test注解的包是org.junit.jupiter.api.Test,则不需要添加@RunWith注解,如果导入的是org.junit.Test,则需要添加,这点需要注意。
Mock 测试代码案例
1、添加依赖
注意:本案例用的 springboot 版本是 2.6 版本
2、编写业务代码
3、Mock 测试
(1)常规测试
先不使用 Mockito ,而是真的去调用一个正常的 Spring bean ,测试类写法如下。其实就是很普通的注入 PositionService bean,然后去调用他的方法。
测试代码:
注意:
- 可以看到测试类中引用的包是 org.junit.jupiter.api.Test 和 org.junit.jupiter.api.BeforeEach,是 jupiter.api 包下面的,此时测试类只用了 @SpringBootTest 这一个注解;
- 但是,如果用的是 org.junit.Test 和 org.junit.Before,测试类上面必须同时用 @RunWith(SpringRunner.class) 和 @SpringBootTest(classes = MySpringbootApplication.class)。必须同时用!!!!!
测试结果:
(2)Mock 测试
Mock 测试需要自定返回结果,结果和方法返回结果类型一致。
语法如下:
使用 Mockito 模拟 Bean 的单元测试代码示例如下:
注意:
- 代码中第一个 Mockito.when 的参数用的是 Mockito.anyString(),表示 任意字符串参数调用 getStr() 方法,就会返回字符串 “刘亦菲”;
- 第二个 Mockito.when 的参数用的是字符串"美女",表示限制只有当参数是 "美女"时,才会返回 “刘亦菲”。
- 因此,在日常 Mock 测试中,通常使用 Mockito.any 作为参数。
(3)Mock 测试常用方法
thenReturn 系列方法
① 定义当调用mock positionService 的 getStr() 方法,并且任意字符串参数时,就返回字符串 “哈哈哈哈”:
表示任意值的参数如下图:
② 定义当调用 mock positionService 的 getStr() 方法,并且限制参数只有是字符串 “美女” 时,才返回字符串 “刘亦菲”:
thenThrow 系列方法
① 当调用 mock positionService 的 getStr() 方法,输入的的参数是 字符串 “9” 时,抛出一个 RuntimeException:
测试结果:
② 如果方法没有返回值的话(即是方法定义为 public void myMethod() {…}),要改用 doThrow() 抛出 Exception:
测试结果:
verify 系列方法
① 检查调用 positionService 的 getStr() 方法,、且参数为 “3” 的次数是否为1次:
② 验证调用顺序,验证 positionService 是否先调用 getStr() 两次,并且第一次的参数是 “3”、第二次的参数是 “5”,然后才调用 getVoid() 方法:
模拟对象有两种方式:
- 对注解了 @Mock 的对象进行模拟MockitoAnnotations.openMocks(this);
- 对单个对象手动 mock :xxx= Mockito.mock(xxx.class);
对 void 的方法设置模拟:
positionService 中有如下方法:
Mock 测试方法:
(4)Mock 测试常用注解
- 全部 Mock
这种方式,serviceA中的所有方法都会被mock,并不会真正被调用到。
- 依赖注入
ServiceA 依赖了 ServiceC 和 DaoA,使用InjectMocks可以自动注入。
- 真实调用
这种方式,调用serviceC的方法,会被真实调用。
Mock测试结合Java反射综合案例
注意:
如果测试的类中有如下配置:
测试代码中需要如下设置配置值:
感谢每一个认真阅读我文章的人!!!
作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。
软件测试面试文档
我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/cjjbc/12258.html