背景
系统是SpringBoot的,原来单元测试用的 Mockito,不过由于版本较低,对静态方法无法Mock,所以又引入了 PowerMock;
好长时间没动过了,现在刚好有一个项目需要写测试代码,顺便也把Mockito升升级;
现在Mockito版本已经到了 5.2.0了,不过需要Java11的支持;目前项目还使用的是Java8,因此只能使用到 Mockito4,最高版本为 4.11.0;
而且从Mockito3的某个版本开始,已经支持了 静态方法的Mock,所以可以暂时抛弃PowerMock了;
依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>${spring-boot.version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>${mockito-version}</version><scope>test</scope></dependency><mockito-version>4.11.0</mockito-version>
其实没必要依赖这么多artifact,也没细究,看例子这样service和http都可以进行Mock测试;
Mock整个类
ClassA,调用到 ClassB , 现在自己Mock生成一个ClassB;
@Component
public class ClassA {@AutowiredClassB classB;public void printA(int value ) {int a;a = value + classB.getValue();System.out.println("------------------------------------------------------");System.out.println("ClassA : a value is " + a);System.out.println("------------------------------------------------------");}}
@Component
public class ClassB {public int getValue() {return 10;}}
测试代码:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest {@AutowiredClassA classA;@MockBeanClassB classB;@Testpublic void Simple() {when(classB.getValue()).thenReturn(5);classA.printA(5);}
}
如果没有Mock掉ClassB,
classA.printA(5) 的结果应该是: 5 + 10 ,结果是 15;
Mock了ClassB,classB.getValue返回从10变成了5,结果应该显示10了
Mock一些方法
有时候需要使用到原始的类,但是只对其中某些方法进行Mock:
@Component
public class ClassA {@AutowiredClassB classB;public int printA(int value ) {int a;a = value + classB.getValue();return a;}public int printB(int value ) {int b;b = value + classB.getValue();return b;}}
正常情况下
@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest2 {@AutowiredClassA classA;@Testpublic void Simple() {System.out.println("printA : " + classA.printA(5) );System.out.println("printB : " + classA.printB(5) );}}
printA(5) 和 printB(5) 结果应该是一样的:
如果想Mock掉ClassA.printA, 让printA返回指定的值,那就需要对printA做处理:
在对printA函数做处理之前,首先要对 classA类进行包装:
方法1 :
@AutowiredClassA classA;改变成:@SpyBeanClassA classA;
或者:
@AutowiredClassA classA;@Testpublic void Simple() {classA = Mockito.spy(classA);。。。。。。}
然后对printA进行处理:
@Testpublic void Simple() {classA = Mockito.spy(classA);when(classA.printA(anyInt())).thenReturn(100);System.out.println("printA : " + classA.printA(5) );System.out.println("printB : " + classA.printB(5) );}
结果:
其中:
when(classA.printA(anyInt())).thenReturn(100);也可以写成doReturn(100).when(classA).printA(anyInt());
静态方法的Mock
现在的Mockito也可以对静态方法做Mock
Bean类:
@Component
public class ClassB {public int getValue() {return 10;}public static int getStaticInt() {return 1;}
}
这里ClassB,多了一个静态方法: getStaticInt
在ClassA中,使用这个静态方法:
@Component
public class ClassA {public int invokeClassBStatic(int value ) {return value + ClassB.getStaticInt();}}
正常情况下,
@SpringBootTest
@RunWith(SpringRunner.class)
public class SimpleTest3 {@AutowiredClassA classA;@Testpublic void Simple() {classA.invokeClassBStatic(5);}
}
的结果是:
现在Mock掉ClassB. getStaticInt,让它返回100,代码变成:
@SpringBootTest
@RunWith(SpringRunner.class)
public class SimpleTest3 {@AutowiredClassA classA;@Testpublic void Simple() {MockedStatic<ClassB> classBMockedStatic = Mockito.mockStatic(ClassB.class);classBMockedStatic.when(() -> ClassB.getStaticInt()).thenReturn(100);System.out.println(classA.invokeClassBStatic(5));}
}
执行结果:
对void函数进行Mock
用 doNoting().when()
用新的函数Mock掉老函数
对printA进行Mock
@RunWith(SpringRunner.class)
@SpringBootTest
public class SimpleTest4 {@SpyBeanClassA classA;@Testpublic void Simple() {classA = Mockito.spy(classA);when(classA.printA(anyInt())).thenAnswer((invocation) -> {int p = invocation.getArgument(0);return p * 2;});System.out.println("printA : " + classA.printA(5));System.out.println("printB : " + classA.printB(5));}}
结果:
被Mock的对象执行原方法:
对于mock的对象,它的所有方法都会被打桩,不执行真实方法,除非使用doCallRealMethod。
Foo mock = mock(Foo.class);
doCallRealMethod().when(mock).someVoidMethod();// 会执行实现方法 Foo.someVoidMethod()
mock.someVoidMethod();
调用原始方法,替换原始方法
ArgumentCaptor<Object> arg1 = ArgumentCaptor.forClass(Object.class);
ArgumentCaptor<Long> arg2 = ArgumentCaptor.forClass(Long.class);// doNothing忽略方法调用,并把方法的2个参数进行捕获
Mockito.doNothing().when(businessService).noReturnMethod1(arg1.capture(), arg2.capture());// 方法调用
String realArg1 = "我是参数1";
long realArg2 = 123567;
businessService.noReturnMethod1(realArg1, realArg2);// 对捕获的参数进行断言
Assert.isTrue(realArg1.equals(arg1.getValue()), "");
Assert.isTrue(realArg2 == arg2.getValue(), "");// void方法测试2,替换void方法
Mockito.doAnswer(invocation -> {Object objArg = invocation.getArgument(1);Long longArg = invocation.getArgument(0);System.out.println(objArg + "===" + longArg);// 对捕获的参数进行断言Assert.isTrue(realArg1.equals(objArg), "");Assert.isTrue(realArg2 == longArg, "");return invocation.callRealMethod();// 需要时,这里可以回调原始方法
}).when(businessService).noReturnMethod2(ArgumentMatchers.anyLong(), ArgumentMatchers.any());businessService.noReturnMethod2(realArg2, realArg1);