java单元测试简介(基于SpringBoot)
mockito
通常,在我们写单测时,会遇到被测试类与外部类有依赖的时候,尤其是与数据库相关的这种费时且有状态的类,很难做单元测试。但好在可以通过“Mockito”这种仿真框架来模拟这些比较费时的类,从而专注于测试某个类内部的逻辑。
mock
@Data
public class Cat {
private String name;
private long id;
private int age;
public void call() {
System.out.println("miao~");
}
public String copy(String str) {
return str;
}
}
import static org.mockito.Mockito.*
@Test
public void testForMock00() throws Exception {
// mock对象
Cat cat = mock(Cat.class);
// 基本类型返回初始值
System.out.println(cat.getAge());
// 包装类型返回null
System.out.println(cat.getName());
// 其行为已和原对象脱离
cat.call();
}
特点:该对象是一个mock对象,其行为已经和原始对象不同,其public方法返回值已被mock,基本对象返回初始值,包装类型返回null。
创建mock对象的另一种方式:@Mock
@Mock
private Cat cat2;
verify
verify方法,可以用来校验mock对象对应的方法是否被调用过,以及具体调用次数
@Test
public void testForMock01() throws Exception {
Cat cat = mock(Cat.class);
cat.setAge(10);
// 校验调用了一次setAge,传参10
verify(cat).setAge(10);
cat.setAge(9);
// 校验调用了一次setAge,传参9
verify(cat, times(1)).setAge(9);
// 校验调用了两次setAge,传参任意
verify(cat, times(2)).setAge(anyInt());
}
stubbing(存根)
当测试的单元依赖这个mock对象的返回值时,我们可以通过提前申明这个函数的返回值来测试各种各样的场景。
提前申明的这个过程被称为存根。
注意点
- 存根时可以被覆盖的(即对一种情况多次存根的话,以最后一次为准),但是不鼓励这么做,可读性会变差。
- 一旦存根后,这个函数会一直返回这个值,不管你调用多少次。
Cat cat = mock(Cat.class);
// 连续存根 调用getAge 第一次返回1 之后返回2
when(cat.getAge()).thenReturn(1,2);
Assert.assertEquals( 1, cat.getAge());
Assert.assertEquals( 2, cat.getAge());
Assert.assertEquals( 2, cat.getAge());
// mock 调用copy 传参 你好 返回 你好
when(cat.copy("你好")).thenReturn("你好");
System.out.println(cat.copy("你好"));
when(cat.copy("包子")).thenReturn("包子");
// 传参饺子,不符合要求,返回null
System.out.println(cat.copy("饺子"));
//传参任意参数,返回包子
when(cat.copy(any())).thenReturn("包子");
System.out.println(cat.copy("你好"));
存根也可以用来指定抛出异常
// 调用getAge 抛出异常
when(cat.getAge()).thenThrow(new UnsupportedOperationException());
cat.getAge();
存根也可以设置回调函数,在回调函数中,我们可以获取到对应方法调用时的入参,以及返回值
@Test
public void testForMock03() throws Exception {
Cat cat = mock(Cat.class);
Answer answer = new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
// 获取调用的方法名:
System.out.println(invocationOnMock.getMethod().getName());
// 获取调用的方法入参:
System.out.println(Arrays.asList(invocationOnMock.getArguments()));
// 设置调用的方法返回值:
return "return is me";
}
};
when(cat.copy(anyString())).thenAnswer(answer);
System.out.println(cat.copy("包饺子"));
}
其他指定行为的:
doNothing() 啥也不干
doCallRealMethod() 调用真正的方法(不代理)
Spy(间谍)
对该对象所有方法的调用都直接调用真实方法,但也可以对某些我们关注的方法进行存根
@Test
public void testForMock04() throws Exception {
Cat cat = new Cat();
cat.setName("喵喵");
Cat spy = spy(cat);
when(spy.getName()).thenReturn("包子");
// 原始对象,真实调用
System.out.println(cat.getName());
// 间谍对象且方法被存根
System.out.println(spy.getName());
// 间谍对象且方法没有被存根
spy.call();
when(spy.copy(anyString())).thenReturn("***");
// spy对象实际也会访问原始对象的方法,并非直接mock
System.out.println(spy.copy("你好"));
}
mock 静态方法
除了实例方法,mockito还可以mock静态方法,具体使用如下。
@Test
public void testForMock05() throws Exception {
Mockito.mockStatic(Cat.class);
when(Cat.getType()).thenReturn("Cat");
System.out.println(Cat.getType());
}