盛年不重来,一日难再晨,及时当勉励,岁月不待人。这篇文章主要讲述Spock框架Mock对象方法经验总结#yyds干货盘点#相关的知识,希望能为你提供帮助。
近期已然陷入了单元测试的汪洋大海,上万行的代码突然要求起来单元测试覆盖率,着实很恐怖的。最经过艰苦的抗争学习之后,终于迈过了技术这个坎儿,特来分享一下最近踩坑的经历,和一些典型的使用场景案例分享。
下面是我使用过的一个常用项目,部分信息隐去了。大家在自己项目中实践的时候可以参考,尽量别直接抄代码,我自己使用过程中有很多兼容性的坑,特别是IDE自动import功能。
技术方案本技术方案基于公司力推的Spock单元测试框架,spock
是一款基于Groovy语言的单元测试框架,其基础也是java
的Junit
,目前最新版已经到了2.0
,但对Groovy
和相应的Java
版本要求较高,所以Groovy版本使用1.+,Spock自带的Mock和Spy足够好了,对于对象行为的模拟满足绝大部分场景,但是涉及静态方法模拟时候存在局限性,所以引入Mockito和PowerMock来实现设计静态方法的测试模拟场景。
以下为相关依赖版本:
<
dependency>
<
groupId>
org.spockframework<
/groupId>
<
artifactId>
spock-core<
/artifactId>
<
version>
1.2-groovy-2.5<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
groupId>
org.spockframework<
/groupId>
<
artifactId>
spock-spring<
/artifactId>
<
version>
1.2-groovy-2.5<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
groupId>
org.powermock<
/groupId>
<
artifactId>
powermock-api-mockito2<
/artifactId>
<
version>
1.7.4<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
groupId>
org.powermock<
/groupId>
<
artifactId>
powermock-module-junit4<
/artifactId>
<
version>
1.7.4<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
groupId>
org.mockito<
/groupId>
<
artifactId>
mockito-core<
/artifactId>
<
version>
2.8.9<
/version>
<
scope>
test<
/scope>
<
/dependency>
因为Spock本身需要Groovy语言支持,所以也需要一个Groovy-all的依赖,请注意注自带的注释:
<
dependency>
<
!-- use a specific Groovy version rather than the one specified by spock-core -->
<
groupId>
org.codehaus.groovy<
/groupId>
<
artifactId>
groovy-all<
/artifactId>
<
version>
2.4.7<
/version>
<
/dependency>
另外,提供的配置文件中多了几项特殊场景下使用的依赖,提供参考:
<
dependency>
<
!-- enables mocking of classes (in addition to interfaces) -->
<
groupId>
net.bytebuddy<
/groupId>
<
artifactId>
byte-buddy<
/artifactId>
<
version>
1.9.9<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
!-- enables mocking of classes without default constructor (together with CGLIB) -->
<
groupId>
org.objenesis<
/groupId>
<
artifactId>
objenesis<
/artifactId>
<
version>
3.0.1<
/version>
<
scope>
test<
/scope>
<
/dependency>
<
dependency>
<
!-- only required if Hamcrest matchers are used -->
<
groupId>
org.hamcrest<
/groupId>
<
artifactId>
hamcrest-core<
/artifactId>
<
version>
2.1<
/version>
<
scope>
test<
/scope>
<
/dependency>
非静态资源由于多个单测框架的方法名重复较多,我把import内容也贴出来了,如果同样的代码无法运行,可以排查一下是否import正确的方法和类。这里不是很建议import static ,因为可能出现混用以及不易排查的问题。
由于目前测试中没有遇到使用Spy放行的逻辑,所以均使用Mock模式,需要对Mock对象的方法进行模拟。这个分为两类:Spock和PowerMock(结合Mockito)。原因是在混合静态资源和非静态资源场景下,指定了PowerMock的
@RunWith
运行规则,不兼容Spock写法,需要用到PowerMock框架Mock对象的功能。Mock被测对象 @Autowired构造方法
用一个controller举例,源代码如下:
@Api(tags = "SLA规则管理模块")
@Slf4j
@RestController
@RequestMapping("/hickwall/v1/static/sla")
public class FunController HttpServletRequest request;
ISlaService service;
@Autowired
public FunController(HttpServletRequest request, ISlaService service)
this.request = request;
this.service = service;
Spock单测代码如下:
import com.funtester.service.ISlaService
import com.funtester.vo.sla.SlaBean
import spock.lang.Shared
import spock.lang.Specificationimport javax.servlet.http.HttpServletRequestclass FunControllerTest extends Specification def service = Mock(ISlaService)@Shared
def request = Mock(HttpServletRequest)def FunController = new FunController(request, service)
@Autowired属性对象,无构造方法
源代码如下:
public class ApiImpl implements IApi @Autowired
private ApiRMapper mapper;
Spock单测部分代码如下:
import com.funtester.mapper.ApiRMapper
import com.funtester.vo.ApiR
import spock.lang.Shared
import spock.lang.SpecificationApiRMapper mapper = Mock(ApiRMapper)def drive = new ApiImpl(mapper:mapper)
PowerMock用法
场景也分为两种:有无构造方法,除了Mock方法不同以外,其他均相同,这里不列举。
PS:如果对象属性中有未被
@Autowired
注释的属性,不能用@AllArgsConstructor
的lombok注解,服务启动会报错。源代码如下:
@Component
@Slf4j
public class TaskScheduled @Autowired
IService service;
@Value("$hickwall.statistic.cid")
publicString cid;
共享对象以及初始化
统一使用Spock提供的功能,用到的注解
@Shared
,不加的话无法在Spock方法中进行赋值操作,但是可以当做一个普通的对象使用。Spock框架Demo:
@Shared
def slaBean = new SlaBean()def setupSpec()
request.getHeader("operator") >
>
"FunTester"
slaBean.name = "测试"
slaBean.quota = 1
slaBean.upstream = "PRO"
slaBean.threshold = 0.1
slaBean.creator = "FunTester"
slaBean.id = 100
定义对象行为 Spock定义Mock对象行为
基础的Spock语法结构
when-then-expct
如下:def "AddSla"()
when:
def sla = FunController.addSla(slaBean)
then:
service.addSla(_) >
>
f ->
assert "FunTester" in f.creator
1expect:sla.code == 0
sla.data =https://www.songbingjia.com/android/= 1
也可以在最开始加上
given
,when
和then
通常一起使用。上述Demo在Mock方法的时候对参数进行了断言和处理,这也是Spock框架的一个特性,其他均为Groovy语法特性。
其他定义Mock行为的语法如下:
service.getAllGroup(_,_) >
>
null//返回null
service.getAllGroup(_,_) >
>
throw new Exception() //抛出异常
service.getAllGroup(_,_) >
>
[]//返回空list,Groovy默认实现ArrayList
service.getAllGroup(_,_) >
>
[slaBean,slaBean]//返回正常list
service.getAllGroup(_,_) >
>
[slaBean,slaBean]//返回正常list
service.getAllGroup(_,10) >
>
[slaBean,slaBean]//定时某个参数
service.getAllGroup(any(),10) >
>
[slaBean,slaBean]//any()等效于_
service.getAllGroup(any(),10) >
>
service.getAllGroup(1,10)//调用其他方法返回
Mockito模拟对象行为
Mockito和PowerMock配合使用语法稍微复杂一些。首先我们需要先定义对象行为(通常在
com.funtesterbase.task.TaskScheduledTest#setupSpec
方法中),然后在用例用使用。定时对象行为:
Mockito.when(newutil.filter(Mockito.any())).thenReturn(true)
定义行为以后,就可以在Spock用例中正常使用,包括在通过Mock对象创建的对象方法中,如果调用到定义过行为的方法,也会走自定义的逻辑。
其他常用定义行为:
Mockito.when(newutil.filter(Mockito.any())).thenReturn(null)
Mockito.when(newutil.filter(Mockito.any())).thenThrow(Exception.class)//抛出异常
PowerMockito.doNothing().when(newutil).filter(Mockito.any(ArrayList.class))//dothing,什么都不做
第三个例子中我们假设
filter
方法是一个无返回的void
方法。【Spock框架Mock对象方法经验总结#yyds干货盘点#】通常我们需要构建返回对象,如果对象需要赋值的属性过多,可以使用初始化赋值的方法,下面是Mock一个返回list的方法返回值的Demo:
Mockito.when(newser.selectAllService()).thenReturn([new NewInterface() setUrl("/abc")
setNname("test")
setMethod("GET"), new NewInterface() setUrl("/abcd")
setNname("test")
setMethod("POST"), new NewInterface() setUrl("/abce")
setNname("test")
setMethod("GET")])
Have Fun ~ Tester !
- FunTester2021年总结
- 2022年度计划手册模板
- 「程序员35岁被淘汰」已经22岁了
- 如何突破职业瓶颈
- XML文件解析实践(DOM解析)
- JsonPath工具类封装
- 移动测试工程师职业
- Selenium 4以后,再不相见的API
- Java线程安全ReentrantLock
- 莫要寻找可能不存在的答案
- 避免PPT自动化的最佳实践
- 千万级日志回放引擎设计稿
推荐阅读
- 我以订披萨为例,给女朋友详细讲了Java设计模式的3种工厂模式
- 逼真,特别逼真的决策树可视化
- Linux之diff命令
- SQLServer2014故障转移群集的部署
- 数据湖是谁(那数据仓库又算什么?)
- #过年不停更#Axios踩坑日记
- 百行代码制作一个属于猫主子的3D场景照片墙#yyds干货盘点#
- #yyds干货盘点#动力节点王鹤Springboot教程笔记Redis
- mysql基础知识点二-mysql sql优化