java 单元测试工具的使用
作为后端开发的测试,个人认为主要关注两个维度:接口测试和单元测试,而接口测试,是否应该在后端项目的测试代码中实现,值得商榷,尤其对于当前的开发潮流,接口应该与具体的实现是无关的,接口测试应该面向接口规范,而前后端分离的模式,接口定义、接口模拟与接口测试的数据的生成规范应该是一致的,可考虑在此维度实现接口测试,相对于维护的问题,也不会带来太多麻烦,所以应该在项目代码层面更多的关注单元测试。这里就 java
语言生态中的单元测试工具进行使用说明。通过搜索引擎以及 Github
对比各个测试工具后,最后选择了 mockito
引入
官网有介绍如何引入,不过官网只说明了引入 mockito-core
,但是实际使用时还需要引入一些其他依赖,比如模拟静态类方法的实现等,常用的依赖包:
byte-buddy
byte-buddy-agent
objenesis
mockito-inline
可在 maven中央仓库
查找并根据自己的项目结构方式进行引入。需要注意的是包的发行方,别使用李鬼包。
使用示例
官网简单的有两个示例:
import static org.mockito.Mockito.*;
// mock creation
List mockedList = mock(List.class);
// using mock object - it does not throw any "unexpected interaction" exception
mockedList.add("one");
mockedList.clear();
// selective, explicit, highly readable verification
verify(mockedList).add("one");
verify(mockedList).clear();
// you can mock concrete classes, not only interfaces
LinkedList mockedList = mock(LinkedList.class);
// stubbing appears before the actual execution
when(mockedList.get(0)).thenReturn("first");
// the following prints "first"
System.out.println(mockedList.get(0));
// the following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
更多示例可在 mockito包文档中 找到提示。一般对于想调用方法的实际逻辑,可使用 spy
,而如果只是想模拟对象,可使用 mock
,实际搭配使用更合适。
实际模拟示例
涉及静态方法、类对象、类方法的模拟。
测试说明:
其中静态类方法定义或者模拟方法的实现都是为了让测试方法中的逻辑正常执行,假如测试方法中没有调用其他类方法(或者调用的类方法没有依赖其他额外的资源,比如数据库等)或者静态类方法,则就不用相关模拟,但考虑对测试方法逻辑测试的严谨性,应对测试方法中调用的其他非私有方法进行模拟,以免影响逻辑测试,其他的非私有方法有自己的单元测试,这样解耦且内聚。
下面是对Intent
的类方法exec
进行的测试,Intent
类属性中有静态属性使用了Context.getSyConf(String,String)
静态方法进行了初始化,而在exec
中的逻辑则调用了类方法getUserListByUserName(String)
以及Context.getRequest().getContextPath()
方法。
// 当要对静态方法进行模拟时需要使用 try(){} 方式,在()中声明定义,在{}进行使用,多个类的静态方法的类声明可在()中使用;分割。
@Test
public void testMethod() throws UnsupportedEncodingException {
// 模拟一个 HttpServletRequest 对象,HttpServletRequest是一个接口
HttpServletRequest request = mock(HttpServletRequest.class);
// 模拟 HttpServletRequest 类方法 getContextPath 的返回值为 /contextPath
when(request.getContextPath()).thenReturn("/contextPath");
// 对 Context.getSyConf(String,String) 与 BaseContext.getRequest() 两个静态方法的模拟,
// 需要注意的是:其中Context继承自 BaseContext,但是BaseContext的静态方法需要在自己上面模拟,而不能模拟Context.getRequest()
try (MockedStatic<BaseContext> mockedBaseContext = mockStatic(BaseContext.class);
MockedStatic<Context> mockedContext = mockStatic(Context.class);
) {
mockedContext.when(() -> Context.getSyConf("SY_HTTP_URL", "")).thenReturn("10.0.17.20:9999");
mockedBaseContext.when(BaseContext::getRequest).thenReturn(request);
// 模拟spy操作对象,Intent是一个类,其中有一些类方法
Intent spyIntent = spy(new Intent());
// 模拟 Intent类的getUserListByUserName 方法
List<String> resultList = new ArrayList<>();
doReturn(resultList).when(spyIntent).getUserListByUserName("张三");
// 调用需要测试的方法。
String resStr = spyIntent.exec("test method param");
assertEquals("success", resStr);
}
}