mock的学习
mock优点
1、前后端分离
让前端工程师独立于后端进行开发。
2、增加测试的真实性
通过随机数据,模拟各种场景。
3、开发无侵入
不需要修改既有代码,就可以拦截Ajax请求,返回模拟的响应数据。
4、用法简单
符合直觉的接口。
5、数据类型丰富
支持生成随机的文本、数字、布尔值、日期、邮箱、链接、图片、颜色等。
6、方便扩展
支持支持扩展更多数据类型,支持自定义函数和正则。
7、在已有接口文档的情况下,我们可以直接按照接口文档来开发,将相应的字段写好,在接口完成 之后,只需要改变url地址即可
添加maven依赖:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>5.8.2</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>4.3.1</scope>
</dependency>
使用:
@InjectMocks–注解修饰的类不能修饰抽象或接口类,被它修饰后,其被mock后的成员变量会注入到@InjectMocks的类中。
Mock使用方式或者技巧
- 在测试类上加注解@RunWith(PowerMockRunner.class),等同于在@Before修饰的before方法中添加MockitoAnnotations.openMocks(this);
- @Spy等同于PowerMockito.spy()方法
- @Mock等同于PowerMockito.mock()方法,但是调用PowerMockito的方法时,必须要调用Whitebox.setInternalState()设置内部依赖。
- verify() 的作用主要是检验我们模拟出来的这个对象中的方法是否成功执行,如果成功执行控制台什么信息都没有,如果没有成功执行,控制台会报错误信息。
上图展示的是手动造数据,走接口流程,覆盖其需要的场景,需要手动造假数据。
小demo说明:(打桩、断言的用法)
/**
* 模拟对象中的某个方法,给这个方法指定一个返回值,那么我们再执行这个模拟对象的方法的时候,返回的值就不再是真实
* 对象返回的值,而应该是我们自己设置的返回值。
*
* 比如我们这里有一个Iterator迭代器,原本调用迭代器对象的next()方法之后返回的值是集合中的下一个元素,我们这里来模拟
* 这个方法的返回值,模拟的是第一次调用next()方法返回值是"hello",第二次调用next()方法返回值是"world",第三次以及往后调用
* next()方法返回值是"abc"
*
* 使用到了when(),thenReturn()方法,第一个thenReturn()代表第一次执行iterator.next()方法的返回值是"hello",
* 第二个thenReturn()代表第二次执行iterator.next()方法的返回值是"world",
* 第三个thenReturn()代表第三次即以后执行iterator.next()方法的返回值都是"abc"
*
* 还是用到了assertEquals(猜想值,变量)断言方法
* */
@Test
public void test2(){
//使用mock模拟出一个Iterator类
Iterator iterator = mock(Iterator.class);
//自己设置迭代器对象方法next()的返回值
when(iterator.next()).thenReturn("hello").thenReturn("world").thenReturn("abc");
//使用mock模拟的iterator对象,去看看iterator调用next()方法之后的返回值是否是我们想的那样
String result = iterator.next() + " " + iterator.next() + " " + iterator.next()+" "+iterator.next();
//使用断言验证猜想的结果是否正确
assertEquals("hello world abc abc",result);
}
expect的用法:
/**
expected此处说明:它觉得test3方法会抛出一个IOException异常,如果test3方法没有抛出该异常,控制台会出现异常信息。
* 使用Mock模拟对象,规定某个方法要抛出一个异常
* */
@Test(expected = IOException.class)
public void test3() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
//我们自己规定当执行OutputStream对象的close()方法的时候,会主动的抛出一个IOException异常
doThrow(new IOException()).when(outputStream).close();
outputStream.close();
}
使用Mock模拟测试某个类中的某个方法(加上参数)调用的次数
/**
* 模拟类中的某个方法(加上对应的参数)调用的次数
*
* times(次数)表示调用几次
* never()表示从没有调用过
* atLeastOnce()表示至少调用多少1次
* atLeast(次数)表示至少调用n次
* atMost(次数)表示最多调用n次
* */
@Test
public void test5(){//验证模拟的list类中的add(1)方法和add(2)和add(3)方法各自调用的次数。
List list = mock(List.class);
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(3);
list.add(3);
//验证是否被调用一次,等效于下面的times(1)
verify(list).add(1);
verify(list,times(1)).add(1);
//验证是否被调用2次
verify(list,times(2)).add(2);
//验证是否被调用3次
verify(list,times(3)).add(3);
//验证是否从未被调用过
verify(list,never()).add(4);
//验证至少调用一次
verify(list,atLeastOnce()).add(1);
//验证至少调用2次
verify(list,atLeast(2)).add(2);
//验证至多调用3次
verify(list,atMost(3)).add(3);
}
使用@Mock注解生成模拟对象
/**
* 使用@Mock注解生成一个模拟对象,注意这样的话必须要在加上一个注解@RunWith(MockitoJUnitRunner.class),
* 要不然的话应用到的mockList或者是iterator会是null值
*/
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest2 {
@Mock//使用mock注解创建模拟对象
private List mockList;
@Mock
private Iterator iterator;
@Test
public void test1(){
mockList.add(1);
verify(mockList).add(1);
}
@Test
public void test2(){
iterator.next();
verify(iterator).next();
}
}