在后台接口开发之后,有必要进行单元测试,可以保证代码的交付质量,同时重构代码得时候,也可以更放心大胆;但是在测试的时候,对于一些不需要的服务,需要使用mock测试来mock函数或者方法;文章做个简单的记录,主要总结下mock,spy的区别,injectMocks的用法,以及PowerMock的使用来解决静态方法的mock;
1.mock和spy的区别
两者都可以注解到某个属性上,区别是用mock注解标注的变量,在调用时,除mock之外的其他方法不会进行调用的,只有声明的mock方法会直接返回给定的值;比如说mock标注在“serviceA”上,同时mock了serviceA中的fun1,如果在测试过程中调用了serviceA的fun2,那么是不会执行fun2的方法的;调用fun1会直接返回mock的返回结果;
spy注解的变量,根据声明的两种方式,当使用doReturn().when时,不进入方法体中调用;而使用when().thenReturn这种方式时,会进行到方法体执行,最后返回声明的结果;如果没有声明方法,那么spy会调用真实的函数的执行流程;下面通过个例子来说明下
定义个类,具体如下所示
public class Main {
public String fun(String s) {
System.out.println(s + " : fun");
fun1(s);
fun2(s);
return null;
}
public void fun1(String s) {
System.out.println(s + " : fun1");
}
private void fun2(String s) {
System.out.println(s + " : fun2");
}
public int getVal(){
return 5;
}
}
先使用@Spy注解
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
public class MainTest {
@Spy
private Main mainClass;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("SSS").when(mainClass).fun(anyString());
}
@Test
public void test() {
String aaa = mainClass.fun("aaa");
System.out.println(aaa);
}
}
使用doReturn().when方法来mock fun函数的返回结果;然后执行 测试test方法,返回结果,打印出sss
SSS
这里说明这种方式没有执行fun函数的内部;
然后修改下mock的声明方式,其它代码不动,修改如下
public class MainTest {
@Spy
private Main mainClass;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mainClass.fun(anyString())).thenReturn("SSS");
}
@Test
public void test() {
String aaa = mainClass.fun("aaa");
System.out.println(aaa);
}
}
执行结果如下所示
: fun
: fun1
: fun2
SSS
说明这种声明的方式,会进入到方法体内部执行,最后再返回结果;
下面再看看mock的测试
public class MainTest {
@Mock
private Main mainClass;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn("SSS").when(mainClass).fun(anyString());
}
@Test
public void test() {
String aaa = mainClass.fun("aaa");
System.out.println(aaa);
}
}
返回结果SSS,说明mock的方法会直接返回;不会进入方法内部;
下面把mock的声明去掉,如下所示
public class MainTest {
@Mock
private Main mainClass;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void test() {
String aaa = mainClass.fun("aaa");
System.out.println(aaa);
}
}
这里会打印null,因为mock的类的方法不会调用的,只有声明的mock方法会直接返回结果;如果改成@Spy来注解,那么会调用真实的方法执行的;这也是mock和spy的区别;
2.InjectMocks
标注为mock或者spy的类可以注入到添加了InjectMocks的类中;举个例子,Service调用Repository中的方法进行数据库操作,我们要mock respository向数据库插入数据的操作,那么就可以
public Class MainTest{
@InjectMocks
private MyService service;
@Mock
private Repository repository;
}
这样service中调用的就是mock的repository中的方法了;但是有个问题,如果service中注入其他的服务,那么其他的服务都是null;需要通过ReflectionTestUtils.setField注入对应的属性才可以;而且InjectMocks和Autowired添加到同一个属性上,有时会存在InjectMock失败的情况,但是InjectMocks失败是没有通知的,这点需要注意下;
3.静态方法的mock
mockito缺点是无法对静态的方法进行mock测试,静态方法的测试需要引入powermock;
testCompile "org.mockito:mockito-core:2.8.9" testCompile "org.powermock:powermock-api-mockito2:1.7.1" testCompile "org.powermock:powermock-module-junit4:1.7.1"
对应的需要引入的jar如图所示,同时需要导入的注解需要进行下面的修改
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.net.ssl.*"})
@PrepareForTest(UserEntityRoleContext.class)
@SpringBootTest(classes = CrmApp.class)
PowerMockito.mockStatic(UserEntityRoleContext.class);
PowerMockito.when(UserEntityRoleContext.getRoles(any())).thenReturn(new HashSet<>(Arrays.asList(new String[]{"aaa", "bbb"})));
mock的时候使用上面的方式来mock静态方法;
https://www.baeldung.com/mockito-annotations
https://blog.csdn.net/qq_30141957/article/details/81273829
https://blog.csdn.net/Aeroleo/article/details/49946999?locationNum=14&fps=1