mockito的入门与使用

链接:

关于mockito的初步学习和总结https://blog.csdn.net/qq_36804701/article/details/80475058


第一步导入,

因为是看汪文君老师的视频学习的,所以首先使用他演示的maven版本

		<dependency>
		  <groupId>org.mockito</groupId>
		  <artifactId>mockito-core</artifactId>
		  <version>1.10.19</version>
		</dependency>

下面有三种方法使用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> 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;
	}

只是反射的一个基本用法,无需赘述

下面是重点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更好,静态方法还是很常用的,如果为了单元测试而特意不使用静态类,那侵入性也太强了,或者只能使用不需要注入的静态方法类。


猜你喜欢

转载自blog.csdn.net/qq_36804701/article/details/80475540