链接:
关于mockito的初步学习和总结https://blog.csdn.net/qq_36804701/article/details/80475058
第一步导入,
因为是看汪文君老师的视频学习的,所以首先使用他演示的maven版本
org.mockito
mockito-core
1.10.19
下面有三种方法使用mock对象
一。原始方法
import org.mockito.Mockito;
import com.kq.highnet2.framework.base.baseUser.service.IBaseUserMainService;
public class LoginInfoActionTest {
private IBaseUserMainService baseUserMainService;
@Before
void init() {
baseUserMainService = Mockito.mock(IBaseUserMainService.class);
}
}
顺便一提在汪文君的视频中我学到了一个有用并好玩的东西,java1.5之后的特性,静态导入
所以可以写成这样
import static org.mockito.Mockito.mock;
import com.kq.highnet2.framework.base.baseUser.service.IBaseUserMainService;
public class LoginInfoActionTest {
private IBaseUserMainService baseUserMainService;
@Before
void init() {
baseUserMainService = mock(IBaseUserMainService.class);
}
}
这样就可以使代码更加优雅和简洁了,特别是mock测试代码中,要调用一大堆的静态方法比如when之类的
二.使用@mock注解
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import com.kq.highnet2.framework.base.baseUser.service.IBaseUserMainService;
public class LoginInfoActionTest {
private @Mock IBaseUserMainService baseUserMainService;
@Before
void init() {
MockitoAnnotations.initMocks(this);
}
}
这样做的好处是不用每一个mockito对象都要mock了
三.使用@Runwith注解
一般来说我会使用最简洁的方式,把环境的工作都交给注解,所以mockito也不例外,连上面那步都不需要
import org.mockito.Mock;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import com.kq.highnet2.framework.base.baseUser.service.IBaseUserMainService;
@RunWith(MockitoJUnitRunner.class)
public class LoginInfoActionTest {
private @Mock IBaseUserMainService baseUserMainService;
@Before
void init() {
}
}
人啊,要克服自己的懒癌,time is precious,make it count。竟然决定了就一定要做完。
mockito的测试类写完了,下面写一些总结的经验教训。
在mock完对象后,下一步就是设置行为,然后注入了,因为设置行为是主要的部分,所以我先讲注入。
mockito其实挺没用的,不能mock私有对象和静态对象,这要powermock才能完成,特别是静态方法,我还专门写了个静态方法类用来保存系统日志的,很得意,不用每个类里面都注入就可以调用,多优雅,现在单元测试时就楞了,我只好暂时注入静态方法用到的service层来使用,不过私有类倒是用反射解决了
Logger logger=LoggerFactory.getLogger(this.getClass());
private @Mock IBaseUserMainService baseUserMainService;
private @Mock IBaseUserRuntimeService baseUserRuntimeService;
private @Mock IBaseCompanyMainService baseCompanyService;
private @Mock ILoginInfoService loginInfoService;
private @Mock HttpServletRequest request;
private @Mock PlatformTransactionManager transactionManager;
private @Mock IBaseSysLogService baseLogService;
LoginInfoAction loginInfoAction = new LoginInfoAction();
@Before
public void setUp() throws Exception {
setField(loginInfoAction, "baseUserMainService", baseUserMainService);
setField(loginInfoAction, "baseUserRuntimeService", baseUserRuntimeService);
setField(loginInfoAction, "baseCompanyService", baseCompanyService);
setField(loginInfoAction, "loginInfoService", loginInfoService);
setField(loginInfoAction, "transactionManager", transactionManager);
setField(loginInfoAction, "baseLogService", baseLogService);
}
public static T setField(T t, String fieldName, Object o) throws Exception{
Field f = t.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(t, o);
return t;
}
只是反射的一个基本用法,无需赘述
2018-9-13 发现了一个更简单方便的方法,现在看来用反射注入实在是蠢
@Mock
MachineRateSettingDao machineRateSettingDao;
@Mock
IBaseUserRuntimeService baseUserRuntimeService;
@InjectMocks
MachineRateSettingMainServiceImpl machineRateSettingMainServiceImpl;
@InjectMocks会自动把前面的Mock对象注入到指定对象中
下面是重点mock方法的行为。
一、有返回值的方法条件判断返回(直接写when是利用了静态导入,后面的代码同,就不再提了)
when(baseUserRuntimeService.checkAppLogin(eq(mobile), eq(password), anyString(), eq(localPhone), eq(type), eq(version)))
.thenReturn(user);
结构非常清晰明了,当方法的参数是指定值得时候,返回指定的对象,eq(mobile)是用来包裹字符串的,因为实际上equals相同的字符串实际上并不一定是相同的对象,而mockito.when需要参数对象相同才能返回对应的值,所以用eq()之后只要字符串equals相同就可以出发条件,值得一提的还有anyString(),这个意思是任何字符串都符合条件,其他类似的还有any()之类的
这里我要吐槽一点,那就是不能对参数进行具体的判断,比如我要第一个参数大于1就返回某个值之类的这种条件判断都不能做,只能固定输入参数来返回,这点感觉太死了,如果有能实现这点的方法欢迎网友指出
二、无返回值的方法判断执行次数
verify(loginInfoService,times(1)).setCurrentConnectFac(factoryId, token);
verify(baseUserRuntimeService,never()).updateAcessKey(token, token);
没有返回值得方法怎么办呢?判断它有没有被执行过,很简单明了,值得注意的是可以判断执行的次数,比如执行两次times(2),或者没有执行过never(),至少执行一次atLeastOnce()等等,这点倒是不错的
三、方法抛出异常
doThrow(new Exception("该公司不存在")).when(loginInfoService).setCurrentConnectFac(factoryId, token);
所有的方法都可以抛出异常
四、指定mock的逻辑
我错了,mockito可以实现对参数进行操作的逻辑,并决定返回值
简单来说就是可以用你写的代码取代真实的代码,下面是我直接转的代码
1 Mockito.doAnswer(new Answer() {
2 @Override
3 public Object answer(InvocationOnMock invocation) throws Throwable {
4 //这里可以获得传给performLogin的参数
5 Object[] arguments = invocation.getArguments();
6
7 //callback是第三个参数
8 NetworkCallback callback = (NetworkCallback) arguments[2];
9
10 callback.onFailure(500, "Server error");
11 return 500;
12 }
13 }).when(mockUserManager).performLogin(anyString(), anyString(), any(NetworkCallback.class));
这样看来是很灵活了,可以获得参数,可以写逻辑,很好
五、使用spy指定方法的特定行为
PasswordValidator spyValidator = Mockito.spy(PasswordValidator.class);
spy和mock在用法上基本一样,除了在初始化时一个是mock()一个是spy(),但是实际上还是不同的,mock对象后,就跟原来的代码毫无瓜葛了,当你没有设定行为时,mock对象只会返回默认值,比如null,也没有任何业务逻辑
但是spy对象在没有设定行为时,它会执行原来的代码而没有任何影响,就是说该走的逻辑还是会走,原来返回什么现在返回什么,但是指定了行为之后,就跟mock对象一样了
以上基本是我在使用中涉及到的用法和心得,实际上我觉得powermock更好,静态方法还是很常用的,如果为了单元测试而特意不使用静态类,那侵入性也太强了,或者只能使用不需要注入的静态方法类。