{
public String answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
Integer num = (Integer) args[0];
if (num > 3) {
return "大于三";
} else {
return "小于三";
}
}
}
```
11 修改对未预设的调用返回默认期望(指定返回值)
```java
/**
* 11 修改对未预设的调用返回默认期望(指定返回值)
*/
@Test
public void test13() throws Exception {
//mock对象使用Answer来对未预设的调用返回默认期望值
List mock = Mockito.mock(List.class, new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return 999;
}
});
//下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
Assert.assertEquals(999, mock.get(1));
//下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
Assert.assertEquals(999, mock.size());
}
```
12 用spy监控真实对象,设置真实对象行为
```java
/**
* 12 用spy监控真实对象,设置真实对象行为
*/
@Test(expected = IndexOutOfBoundsException.class)
public void spy_on_real_objects(){
List list = new LinkedList();
List spy = Mockito.spy(list);
//下面预设的spy.get(0)会报错,因为会调用真实对象的get(0),所以会抛出越界异常
//Mockito.when(spy.get(0)).thenReturn(3);
//使用doReturn-when可以避免when-thenReturn调用真实对象api
Mockito.doReturn(999).when(spy).get(999);
//预设size()期望值
Mockito.when(spy.size()).thenReturn(100);
//调用真实对象的api
spy.add(1);
spy.add(2);
Assert.assertEquals(100,spy.size());
Assert.assertEquals(1,spy.get(0));
Assert.assertEquals(2,spy.get(1));
Assert.assertEquals(999,spy.get(999));
}
```
13 不做任何返回
```java
/**
* 13 不做任何返回
*/
@Test
public void test14() {
A a = Mockito.mock(A.class);
//void 方法才能调用doNothing()
Mockito.doNothing().when(a).setName(Mockito.anyString());
a.setName("bb");
Assert.assertEquals("bb", a.getName());
}
class A {
private String name;
private void setName(String name) {
this.name = name;
}
private String getName() {
return name;
}
}
```
14 调用真实的方法
```java
/**
* 14 调用真实的方法
*/
@Test
public void Test() {
B a = Mockito.mock(B.class);
//void 方法才能调用doNothing()
Mockito.when(a.getName()).thenReturn("bb");
Assert.assertEquals("bb",a.getName());
//等价于Mockito.when(a.getName()).thenCallRealMethod();
Mockito.doCallRealMethod().when(a).getName();
Assert.assertEquals("zhangsan",a.getName());
}
class B {
public String getName(){
return "zhangsan";
}
}
```
15 重置mock
```java
/**
* 15 重置mock
*/
@Test
public void reset_mock(){
List list = mock(List.class);
Mockito. when(list.size()).thenReturn(10);
list.add(1);
Assert.assertEquals(10,list.size());
//重置mock,清除所有的互动和预设
Mockito.reset(list);
Assert.assertEquals(0,list.size());
}
```
16 @Mock注解
```java
/**
* 16 @Mock 注解
*/
public class MockitoTest {
@Mock
private List mockList;
//必须在基类中添加初始化mock的代码,否则报错mock的对象为NULL
public MockitoTest() {
MockitoAnnotations.initMocks(this);
}
@Test
public void AnnoTest() {
mockList.add(1);
Mockito.verify(mockList).add(1);
}
}
```
17 指定测试类使用运行器:MockitoJUnitRunner
```java
/**
* 17 指定测试类使用运行器:MockitoJUnitRunner
*/
@RunWith(MockitoJUnitRunner.class)
public class MockitStudy03 {
@Mock
private List mockList;
@Test
public void AnnoTest() {
mockList.add(1);
Mockito.verify(mockList).add(1);
}
}
```
18 @MockBean
特点
**1 可以使用接口,也可以使用具体的接口实现类**
**2 对代理对象的Bean兼容也可以,因为本质上@Mock|@MockBean 是一个虚拟对象,没有stub就返回默认值**
**3 如果对象是代理对象,SpringBean 走真实调用还是无法解决成功,无论是否引入mock-line 2.7xxx**
```java
@Controller
public class DemoAction {
public String getUserName(String id) {
return id+"张三";
}
}
/**
* 18 @MockBean
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class MockBean01 {
/**
* 项目启动时排除掉的bean
* 替换为Mockit操作的Bean
* 是集成在Spring容器中的 @Mock 用来替换Bean对象
* @MockBean 只能 mock 本地的代码——或者说是自己写的代码,对于储存在库中而且又是以 Bean 的形式装配到代码中的类无能为力。
*/
@MockBean
private DemoAction demoAction;
@Test
public void testGetUserName() throws Exception {
Mockito.when(demoAction.getUserName("1")).thenReturn("李四");
System.out.println(demoAction.getUserName("1"));
}
https://www.jianshu.com/p/ecbd7b5a2021
```
` @MockBean` 只能 mock 本地的代码——或者说是自己写的代码,对于储存在库中而且又是以 Bean 的形式装配到代码中的类无能为力
`@SpyBean` 解决了 SpringBoot 的单元测试中 `@MockBean` 不能 mock 库中自动装配的 Bean 的局限
19 spy 与 mock的不同
**spy 和 mock不同,不同点是:**
**spy 的参数是对象示例,mock 的参数是 class。 被 spy 的对象,调用其方法时默认会走真实方法。mock 对象不会。**
**spy 如果没有打桩的方法会走真实的调用对象,如果打桩了会自走打桩的操作,本质上还会走一次**
**mock 没有打桩就返回默认返回值,不走真实方法 打桩了就走打桩的操作**
```java
/**
* 描述: describe
* 19 spy 与 mock的不同
* @author shengyang5
* @version 2021/7/22 15:03
*/
class ExampleService {
int add(int a, int b) {
System.out.println("被调用");
return a + b;
}
}
public class SpyStudy01 {
/**
* spy 和 mock不同,不同点是:
*
* spy 的参数是对象示例,mock 的参数是 class。
* 被 spy 的对象,调用其方法时默认会走真实方法。mock 对象不会。
*
* spy 如果没有打桩的方法会走真实的调用对象,如果打桩了会自走打桩的操作,本质上还会走一次
*
* mock 没有打桩就返回默认返回值,不走真实方法
* 打桩了就走打桩的操作
*/
@Test
public void test() throws Exception {
ExampleService spy = spy(new ExampleService());
// 没有打桩 默认走真实方法
int add = spy.add(1, 2);
Assert.assertEquals(3, add);
System.out.println("add = " + add);
// 打桩后 不走了,但是实际上还是会先执行一次 用when去设置模拟返回值时,它里面的方法(spy.add())会先执行一次。
when(spy.add(Mockito.anyInt(), Mockito.anyInt())).thenReturn(10);
add = spy.add(1, 2);
System.out.println("add = " + add);
Assert.assertEquals(10, add);
}
}
```
20 @Spy 的缺陷
**1mockito 1.0版本中使用@Spy监视接口类,会报错,2.0版本中不会报错,但是不支持接口类、内部类、抽象类(这是个大坑)**
**2对于@Spy,如果发现修饰的变量是 null,会自动调用类的无参构造函数来初始化**
**3` @Spy` 如果是修饰接口的话,不能调用的到真实的方法,而且还不报错**
**4如果要真实模拟,必须指定到接口的实现类,而且实现类的其他 资源类,你也需要手动的@mock或者@spy加入**
**5不能自动的处理类里的Bean的依赖关系**
```java
package com.codeyang.mocktest2.mockit;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.Mockito.*;
/**
* 描述:
* 20 @Spy 的缺陷
*
* @author shengyang5
* @version 2021/7/22 15:26
*/
class ExampleService2 {
int add(int a, int b) {
return a + b;
}
}
public class SpyStudy02 {
/**
* mockito 1.0版本中使用@Spy监视接口类,会报错,2.0版本中不会报错,但是不支持接口类、内部类、抽象类(这是个大坑)
* 对于@Spy,如果发现修饰的变量是 null,会自动调用类的无参构造函数来初始化
*
* 注意:
* @Spy 如果是修饰接口的话,不能调用的到真实的方法,而且还不报错
* 如果要真实模拟,必须指定到接口的实现类,而且实现类的其他 资源类,你也需要手动的@mock或者@spy加入
* 不能自动的处理类里的Bean的依赖关系
*/
/**
* 写法1
*
* @Spy private ExampleService spyExampleService;
* 写法2
* @Spy private ExampleService spyExampleService = new ExampleService();
* 注意: 如果没有无参的构造方法那么就要手动new对象
*/
@Spy
private ExampleService2 spyExampleService2;
@Test
public void test_spy() {
MockitoAnnotations.initMocks(this);
Assert.assertEquals(3, spyExampleService2.add(1, 2));
when(spyExampleService2.add(1, 2)).thenReturn(10);
Assert.assertEquals(10, spyExampleService2.add(1, 2));
}
/**
* @Spy 可以修饰接口,抽象类等 但是调用方法不报错,返回默认值
*/
@Spy
private Isa isa;
@Test
public void isaTest() {
MockitoAnnotations.initMocks(this);
String name = isa.loadName();
System.out.println(name);
}
/**
* @Spy 本身需要支持到接口具体实现类
*/
@Spy
private IsaImpl isaImpl;
@Test
public void isaImplTest() {
MockitoAnnotations.initMocks(this);
String name = isaImpl.loadName();
System.out.println(name);
}
}
interface Isa {
String loadName();
}
class IsaImpl implements Isa {
@Override
public String loadName() {
return "1111";
}
}
```
21 @SpyBean 与 Spring框架+Mockit集成使用
**1使用SpringRunner 就可以得到Spring的容器的上下文**
**2SpringTest 是走SpringTest的一套流程**
`@SpyBean`
**1 继承了@Spy的特性 方法调用没有打桩走真实第哦啊有**
**2 比@Spy更强可以兼容普通的Bean对象创建的对象 可以调用非代理对象的真实方法**
**(测试对于JDK的动态代理对象兼容不行,CGLIB的没有测试)**
**3 对于Mybatis等动态代理的对象 要支持必须要引入mockito-inline 2.7 以上**
**4 @SpyBean 比 @Spy 更智能,可以自动主力类其他资源的依赖关系,并自动加入**
**5 @Spy 只支持到具体的类 @SpyBean可以支持接口-可以把spring的bean对象自动 spy**
```java
package com.codeyang.mocktest2.mockit;
import com.codeyang.mocktest2.entity.TUser;
import com.codeyang.mocktest2.mapper.TUserMapper;
import com.codeyang.mocktest2.service.TUserService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* 描述:
* 21 RunWith 是启动什么上下文
* 1使用SpringRunner 就可以得到Spring的容器的上下文
* 2SpringTest 是走SpringTest的一套流程
*
* @author shengyang5
* @version 2021/7/22 15:29
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpyBeanStudy01 {
/**
* 如果要使用@Mock @Spy 原生注解需要前置启动
*/
@Before
public void Before() throws Exception {
MockitoAnnotations.initMocks(this);
}
/**
* @SpyBean |
* 1 继承了@Spy的特性 方法调用没有打桩走真实第哦啊有
* 2 比@Spy更强可以兼容普通的Bean对象创建的对象 可以调用非代理对象的真实方法
* (测试对于JDK的动态代理对象兼容不行,CGLIB的没有测试)
* 3 对于Mybatis等动态代理的对象 要支持必须要引入mockito-inline 2.7 以上
* 4 @SpyBean 比 @Spy 更智能,可以自动主力类其他资源的依赖关系,并自动加入
* 5 @Spy 只支持到具体的类 @SpyBean可以支持接口-可以把spring的bean对象自动 spy
*/
// @Spy // 如果是@Spy 就会报错,因为其 它无法将 userService 里的 TUserMapper处理
// private TUserServiceImpl userService;
@SpyBean
private TUserService userService;
@Test
public void userServiceQueryAll() throws Exception {
List tUsers = userService.queryAll();
System.out.println(tUsers);
}
/**
* 对于动态代理对象无法直接mock 需要引入第三方jar mock-inline
*/
@SpyBean
private TUserMapper userMapper;
@Test
public void userMapperQueryAll() throws Exception {
List tUsers = userMapper.selectAll();
System.out.println(tUsers);
}
}
```
==@InjectMocks==
**模拟创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。**
**1 InjectMocks 对象 代表这个对象可以被打桩**
**2一般修饰被测试的类**
**3InjectMocks 创建这个类的对象并自动将@Mock ,@Spy 等注解的属性值注入到这个中**
**4要求必须是类不能是接口**
```
1、Mockito部分:
a. @InjectMocks:模拟创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。
b. @Mock、@Spy:两者均表示mock对象,但是用法有区别
(1). @Mock表示对函数的调用均执行mock(即虚假函数),不执行真正部分,如果未对调用链mock,调用时将返回默认值(null、false、0)
(2). @Spy表示对函数的调用均执行真正部分,只有mock的调用链才会走mock,未mock打桩的调用链,走真实调用
(3). mockito 1.0版本中使用@Spy监视接口类,会报错,2.0版本中不会报错,但是不支持接口类、内部类、抽象类(这是个大坑)
c. when方法:
(1). when方法不会孤独使用,一般结合stub打桩同时使用
(2). when方法是作为mock方法时的匹配条件
(3). when方法可结合doCallRealMethod()、doReturn()、thenReturn()等方法使用
(4). when()对调用方法mock时,可传入具体的参数值,也可使用匹配器(比如:anyString()、any()、anyInt()、isNull()、isNotNull()等)
(5). 如果调用方法传入的是dto、model之类的对象,那么在不修改开发代码的基础上,可以使用argThat()自定义参数匹配器(核心使用方法),重写匹配器方法
(6). 如果接口请求是post,且入参是对象,传参是json串,那么序列化后,会导致参数传入controller层接口时,内存地址变了,而mockito默认的是使用equals方法匹配,
此时内存地址已变,会导致when()mock条件匹配失败,此时需要使用argThat()自定义参数匹配器,具体实现见下面示例
d. stub打桩:
(1). 具体值打桩,常用方法有doReturn()、thenReturn(),两者使用场景不同,前者用于@Spy场景,后者用于@Mock场景,且两者在when的前后顺序不一样
(2). 特殊打桩, doCallRealMethod() -→ 走实际真实调用链
doNothing() -→ 什么事都不做,不进行任何打桩(常用语void调用方法)
doAnswer() -→ 和doReturn很相似
doThrow() -→ 打桩抛出异常
2、MockMvc部分:
a. 测试逻辑
(1). MockMvcBuilder构造MockMvc的构造器
(2). MockMvcRequestBuilders创建request请求(包括请求头部信息、入参、cookis)
(3). mockMvc调用perform,执行一个RequestBuilder请求,调用controller的业务处理逻辑
(4). perform返回ResultActions,返回操作结果,可以通过ResultActions、MockMvcResultMatchers、MvcResoult、Junit等方式对结果进行打印、验证
b. 方法细解
(1). perform:执行一个RequestBuilder请求,会自动执行SpringMvc的流程并映射到相应的控制器执行处理
(2). andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断)
(3). andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断)
(4). andReturn:最后返回相应的MvcResult:然后进行自定义验证/进行下一步异常处理(对返回的数据进行的判断)
(5). param:添加request的参数,如发送请求的时候戴上了schoolId=10001的参数,可用于get请求,假如使用需要发送json数据格式的时候将不能使用这种方式(比如post请求)
(6). content:添加request参数,string格式,可用于post请求,传送json串参数
c. 两种不同构造MockMvc构造器的方法
(1). 集成web环境方式
MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc
(2). 独立测试方式
MockMvcBuilders.standaloneSetup(Object... controllers):通过参数指定一组控制器,这样就不需要从上下文获取了
```
```java
/**
* 描述: 22 @InjectMocks
*
* @author shengyang5
* @version 2021/7/23 13:49
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class InjectMocksTest {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
/**
* 如果@InjectMocks 是一个接口 需要 new 指定类
* 不能是接口
*/
@InjectMocks
// 不注入也可以
@Autowired
private MvcController mvcController;
@SpyBean
private TUserService tUserService;
@Test
public void getAll() {
List tUsers = new ArrayList<>();
tUsers.add(new TUser(999, "测试数据", 99, "古巴"));
when(tUserService.queryAll()).thenReturn(tUsers);
System.out.println(mvcController.getAll());
if (tUsers.equals(mvcController.getAll())) {
System.out.println(true);
}
}
}
```
## MockMvc
#### **背景**
MockMvc 是SpringMvc 也就是SpringWeb解决方案中的一环,设计目的就是为了针对Web层的测试,是针对SpringMvc的测试不同于Junit针对Service层的测试
本质上是拿到了SpringMvc的WebContenxt 是天然集成在WebTest的jar包中
就是SpringMvc下面的一个类
MockMvc是由spring-test包提供,实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,使得测试速度快、不依赖网络环境。同时提供了一套验证的工具,结果的验证十分方便。
#### 为什么使用
普通的原始测试 启动容器-->浏览器请求-- 最耗时操作
PostMan模拟数据 启动容器---> postman构造请求
不需要我们单独引入,可以在不启动web容器的情况下也可以直接使用web层的处理器来测试,直接使用代码方式来测试
Mockvc 不会收到网络环境的波动影响,不依赖网络,在分布式开发中这个测试就有很大的优点
#### 优点
MockMvc是springMvc其下的类,pom文件中不需要过多的额外依赖
MockMvc对http接口请求,无需启动本地服务,节省时间,且方便测试后的代码修改
MockMvc提供了灵活的mvc环境模拟的方式,包括独立测试方式、集成web环境方式
MockMvc支持get、post、delete、put、file文件上传等多种接口请求方式
MockMvc支持session,且可以直接跨过鉴权,可以无需编写鉴权部分
MockMvc实现了对Http请求的模拟,能够直接使用网络的形式,转换到Controller的调用,这样可以使得测试速度快、不依赖网络环境,
而且提供了一套验证的工具,这样可以使得请求的验证统一而且很方便
使用MockMvc模拟controller请求,无需启动本地服务
#### 用法
接口MockMvcBuilder,提供一个唯一的build方法,用来构造MockMvc。主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应两种测试方式,即独立安装和集成Web环境测试(并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。MockMvcBuilders提供了对应的创建方法standaloneSetup方法和webAppContextSetup方法,在使用时直接调用即可。
#### 注意
**如果接口请求是post,且入参是对象,传参是json串,那么序列化后,会导致参数传入controller层接口时,内存地址变了,而mockito默认的是使用equals方法匹配,**
**此时内存地址已变,会导致when()mock条件匹配失败,此时需要使用argThat()自定义参数匹配器,**
#### a. 测试逻辑
(1). MockMvcBuilder构造MockMvc的构造器
(2). MockMvcRequestBuilders创建request请求(包括请求头部信息、入参、cookis)
(3). mockMvc调用perform,执行一个RequestBuilder请求,调用controller的业务处理逻辑
(4). perform返回ResultActions,返回操作结果,可以通过ResultActions、MockMvcResultMatchers、MvcResoult、Junit等方式对结果进行打印、验证
#### b. 方法细解
(1). perform:执行一个RequestBuilder请求,会自动执行SpringMvc的流程并映射到相应的控制器执行处理
(2). andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确(对返回的数据进行的判断)
(3). andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台(对返回的数据进行的判断)
(4). andReturn:最后返回相应的MvcResult:然后进行自定义验证/进行下一步异常处理(对返回的数据进行的判断)
(5). param:添加request的参数,如发送请求的时候戴上了schoolId=10001的参数,可用于get请求,假如使用需要发送json数据格式的时候将不能使用这种方式(比如post请求)
(6). content:添加request参数,string格式,可用于post请求,传送json串参数
#### c. 两种不同构造MockMvc构造器的方法
(1). 集成web环境方式
MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc
(2). 独立测试方式
MockMvcBuilders.standaloneSetup(Object... controllers):通过参数指定一组控制器,这样就不需要从上下文获取了
#### MockMvc案列代码
```java
/**
* 描述: describe
*
* @author shengyang5
* @version 2021/7/21 10:23
*/
//SpringBoot1.4版本之前用的是SpringJUnit4ClassRunner.class
@RunWith(SpringRunner.class)
//SpringBoot1.4版本之前用的是@SpringApplicationConfiguration(classes = Application.class)
@SpringBootTest
//测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的
@WebAppConfiguration
public class MockMvcTest {
/**
* 注入 SpringWebContext
*/
@Autowired
private WebApplicationContext webApplicationContext;
/**
* MockMvc对象
*/
private MockMvc mockMvc;
// 1 前提配置mockmvc
@Before
public void setup() {
//两种实例化的方式
// 方式1 独立安装
// mockMvc = MockMvcBuilders.standaloneSetup(new MvcController()).build();
//方式2 集成web
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
// 2 构建请求测试web
/**
* 最简单的请求构造
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = MockMvcRequestBuilders.get("/api/get");
// 在执行mokmvc 之前将请求先放入
mockMvc.perform(mockHttpServletRequestBuilder);
}
/**
* 1
* 请求 /api/getByid
* 传参 9
* 打印 请求响应报文全部内容
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
RequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/api/getByid")
.param("id", "9");
mockMvc.perform(requestBuilder)
.andDo(MockMvcResultHandlers.print());
}
/**
* 2
* 请求 /api/getByid
* 传参 9
* 拿到请求的对应响应内容
*
* @throws Exception
*/
@Test
public void testGet() throws Exception {
/*
* 1、mockMvc.perform执行一个请求。
* 2、MockMvcRequestBuilders.get("XXX")构造一个请求。
* 3、ResultActions.param添加请求传值
* 4、ResultActions.accept(MediaType.TEXT_HTML_VALUE))设置返回类型
* 5、ResultActions.andExpect添加执行完成后的断言。
* 6、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情
* 比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
* 7、ResultActions.andReturn表示执行完成后返回相应的结果。
*/
RequestBuilder requestBuilder = MockMvcRequestBuilders
.get("/api/getByid")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "9")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE);
ResultActions resultActions = mockMvc.perform(requestBuilder)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
System.out.println(resultActions.andReturn().getResponse().getContentAsString());
}
/**
* 3
* 简化写法使用静态导入
*
* @throws Exception
*/
@Test
public void get3() throws Exception {
// 简化写法,可以导入静态方法
ResultActions resultActions = mockMvc.perform(
get("/api/getByid")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "9")
.accept(MediaType.APPLICATION_JSON_UTF8)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
System.out.println("返回结果=" + resultActions.andReturn().getResponse().getContentAsString());
}
/**
* 4
* 测试restfull风格
*
* @throws Exception
* @RequestParam 写法
*/
@Test
public void get4() throws Exception {
ResultActions resultActions = mockMvc.perform(
get("/api/getId")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.param("id", "9")
.accept(MediaType.APPLICATION_JSON_UTF8)
)
.andExpect(MockMvcResultMatchers.status().isOk())
.andDo(MockMvcResultHandlers.print());
System.out.println("返回结果=" + resultActions.andReturn().getResponse().getContentAsString());
}
/**
* 4
* 测试restfull风格
* 测试@PathVariable
*
* @throws Exception
*/
@Test
public void get5() throws Exception {
ResultActions resultActions = mockMvc.perform(
get("/api/get/9")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.accept(MediaType.APPLICATION_JSON_UTF8)
)
.andExpect(status().isOk())
.andDo(print());
System.out.println("返回结果=" + resultActions.andReturn().getResponse().getContentAsString());
}
/**
* post请求
*
* @throws Exception
*/
@Test
public void test6() throws Exception {
// 最简化写法
System.out.println(mockMvc.perform(
post("/api/getNameAndAdd") // 请求地址
.param("name", "小王") // 参数 param
.param("address", "安徽")
.accept(MediaType.APPLICATION_JSON_UTF8) // 期望响应什么类型
).andExpect(status().isOk()) // expect 校验
.andDo(print()) // 请求结束后执行什么操作
.andReturn().getResponse().getContentAsString()); // 返回响应信息
}
/**
* json 请求
* put 方式
*
* @throws Exception
*/
@Test
public void test7() throws Exception {
// 测试请求是json数据格式
ObjectMapper objectMapper = new ObjectMapper();
TUser tUser = objectMapper.readValue("{\"id\":7,\"name\":\"小王老头\",\"age\":20,\"address\":\"安徽\"}", TUser.class);
tUser.setAddress("北京");
String jsonStr = objectMapper.writeValueAsString(tUser);
MvcResult mvcResult = mockMvc.perform(
put("/api/putObj")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonStr)
.accept(MediaType.APPLICATION_JSON_UTF8)
)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
/**
* 测试文件上传
*
* @throws Exception
*/
@Test
public void test() throws Exception {
FileInputStream inputStream = new FileInputStream("D:\\CodeWorkSpace\\mocktest2\\src\\main\\resources\\file.txt");
byte[] bytes = toByteArray(inputStream);
MockMultipartFile firstFile = new MockMultipartFile("uploadFile", "file.txt", "text/plain", bytes);
ResultActions resultActions = mockMvc.perform(fileUpload("/api/upload")
.file(firstFile))
.andExpect(status().isOk())
.andDo(print());
System.out.println("fileContent = " + resultActions.andReturn().getResponse().getContentAsString());
}
/**
* InputStream 转换成byte[]
*
* @param input
* @return
* @throws IOException
*/
private static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
}
```
## 最终极版本合集测试案列
```java
/**
* 描述: describe
*
* @author shengyang5
* @version 2021/7/23 14:27
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@WebAppConfiguration
public class BaseUnitTest {
protected MockMvc mockMvc;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void emptyTest() {
// 由于自动test必须有方法,所以加一个空的Test方法
}
}
/**
* 描述: Mockito+MockMvc+Junit 自动集成测试
* SpringMVC单元测试样例代码
*
* @author shengyang5
* @version 2021/7/23 14:26
*/
public class MockitoMockMvcJunitTest extends BaseUnitTest {
@InjectMocks
private MvcController mvcController;
/**
* MvcController 需要 userService
* 所以注入
* userService
* 使用@SpyBean注入 走真实调用
*/
@SpyBean
private TUserService userService;
@Override
public void setup() throws Exception {
super.setup();
mockMvc = MockMvcBuilders.standaloneSetup(mvcController).build();
}
/**
* post请求请求的相关操作
*
* @throws Exception
*/
@Test
public void testPost() throws Exception {
//mock 操作
List list = new ArrayList<>();
list.add(new TUser(99, "还在开发中", 99, "湖南"));
// 模拟这个 需要被调用的方法,这个接口的方法可能是RPC远程调用比较耗时,也可能还没有开发完成,我们直接模拟出来,
when(userService.queryAddressAndName(Mockito.anyString(), Mockito.anyString()))
.thenReturn(list);
// 构建请求
MockHttpServletRequestBuilder post = post("/api/getNameAndAdd") // 请求地址
.param("name", "小王") // 参数 param
.param("address", "安徽") // 参数 param
.contentType(MediaType.APPLICATION_JSON_UTF8)
.accept(MediaType.APPLICATION_JSON_UTF8);// 期望响应什么类型
// 执行请求
ResultActions resp = mockMvc.perform(post);
// 对结添加预期
String result = resp
// 期望放回状态
.andExpect(status().isOk())
// 打印请求报文
.andDo(print())
// 得到响应内容
.andReturn().getResponse().getContentAsString();
System.out.println("result = " + result);
}
/**
* 测试put
*
* @throws Exception
*/
@Test
public void testPut() throws Exception {
// 1 构造模拟请求测试需要的输入数据
ObjectMapper objectMapper = new ObjectMapper();
TUser tUser = new TUser(100, "还在开发中put", 20, "shangHai");
String jsonStr = objectMapper.writeValueAsString(tUser);
// 2 构建中间步骤 的返回数据
// 这里涉及到了JSON数据的序列化,需要手动写请求参数匹配
when(userService.updateUser(Mockito.argThat(new IsaMatcher()))).thenReturn(1);
MvcResult mvcResult = mockMvc.perform(
put("/api/putObj")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(jsonStr)
.accept(MediaType.APPLICATION_JSON_UTF8)
)
.andExpect(status().isOk())
.andDo(print())
.andReturn();
System.out.println(mvcResult.getResponse().getContentAsString());
}
class IsaMatcher implements ArgumentMatcher {
@Override
public boolean matches(TUser argument) {
//这里就可以规定好对象的比对操作
// 无脑返回true
return true;
}
}
}
```
项目案列的demo
参考资料如下:
1. https://www.jianshu.com/p/ecbd7b5a2021
2. https://blog.csdn.net/qq_28988969/article/details/105496849
3. https://docs.spring.io/spring-framework/docs/4.1.8.RELEASE_to_4.1.9.RELEASE/Spring%20Framework%204.1.9.RELEASE/org/springframework/test/web/servlet/MockMvc.html
4. https://blog.csdn.net/wo541075754/article/details/88983708