Spock单元测试框架实战指南六-静态⽅法测试
本篇主要讲解Spock如何扩展第三⽅Power Mock对静态⽅法进⾏测试
实现原理
前⾯的⽂章讲到Spock的单测代码是继承⾃Specification基类,⽽Specification⼜是基于Junit的注解@RunWith()实现的,代码如下:
@RunWith(Sputnik.class)
@SuppressWarnings("UnudDeclaration")
public abstract class Specification extends MockingApi {
powermock的PowerMockRunner也是继承⾃Junit,所以使⽤powermock的@PowerMockRunnerDelegate()注解可以指定Spock的⽗类Sputnik去代理运⾏power mock,这样就可以在Spock ⾥使⽤powermock去模拟静态⽅法、final⽅法、私有⽅法等
其实Spock⾃带的GroovyMock可以对groovy⽂件的静态⽅法mock,但对Java代码的⽀持不完整,只能mock当前Java类的静态⽅法,官⽅给出的解释如下:
()
因为我们项⽬中存在很多调⽤静态⽅法的代码,现阶段考虑重构业务代码的成本过⾼,所以这⾥使⽤扩展power mock的⽅式测试静态⽅法
Spock 代理 Power Mock
先看下需要测试的业务代码⽰例:
public UrVO getUrByIdStatic(int uid){
List<UrDTO> urs = UrInfo();
UrDTO urDTO = urs.stream().filter(u -> u.getId() == uid).findFirst().orEl(null);
UrVO urVO = new UrVO();
if(null == urDTO){
return urVO;
}
urVO.Id());
urVO.Name());
urVO.Sex());
if("上海".Province())){
urVO.tAbbreviation("沪");
urVO.tPostCode(200000);
}
if("北京".Province())){
urVO.tAbbreviation("京");
urVO.tPostCode(100000);
}
if(null != Telephone() && !"".Telephone())){
urVO.Telephone().substring(0,3)+"****"+Telephone().substring(7));
}
// 静态⽅法调⽤⾝份证⼯具类
Map<String, String> idMap = IdNo());
urVO.("age")!=null ? Integer.("age")) : 0);
// 静态⽅法调⽤记录⽇志
LogUtils.info("respon ur:", String());
return urVO;
}
在倒数第4⾏和倒数第2⾏代码分别调⽤了 "⾝份证⼯具类BirAgeSex()" 和 "LogUtils.info()" ⽇志记录的⽅法,如果要对这两个静态⽅法进⾏mock,我们可以使⽤Spock+power mock的⽅式:
import org.junit.runner.RunWith
kito.Mockito
import org.kito.PowerMockito
import lassloader.annotations.PrepareForTest
import lassloader.annotations.SuppressStaticInitializationFor
import dules.junit4.PowerMockRunner
import dules.junit4.PowerMockRunnerDelegate
import org.spockframework.runtime.Sputnik
import spock.lang.Specification
/**
* 测试静态⽅法mock
* @Author:
* @Description: 公众号:Java⽼K
* @Date: Created in 20:53 2020/7/16
* @Modified By:
*/
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest([LogUtils.class, IDNumberUtils.class])
@SuppressStaticInitializationFor(["com.javakk.spock.util.LogUtils"])
class UrServiceStaticTest extends Specification {
def processor = new UrService()
def dao = Mock(UrDao)
void tup() {
processor.urDao = dao
// mock静态类
}
def "GetUrByIdStatic"() {
given: "设置请求参数"
def ur1 = new UrDTO(id:1, name:"张三", province: "上海")
def ur2 = new UrDTO(id:2, name:"李四", province: "江苏")
def idMap = ["birthday": "1992-09-18", "x": "男", "age": "28"]
and: "mock掉接⼝返回的⽤户信息"
and: "mock静态⽅法返回值"
PowerMockito.BirAgeSex(Mockito.any())).thenReturn(idMap)
when: "调⽤获取⽤户信息⽅法"
def respon = UrByIdStatic(1)
then: "验证返回结果是否符合预期值"
with(respon) {
name == "张三"
abbreviation == "沪"
postCode == 200000
age == 28
}
}
}
在UrServiceStaticTest类的头部使⽤@PowerMockRunnerDelegate(Sputnik.class)注解,交给Spoc
k代理执⾏,这样既可以使⽤ Spock + groovy 的各种功能,⼜可以使⽤power mock的对静态,final等⽅法的mock
@SuppressStaticInitializationFor(["com.javakk.spock.util.LogUtils"])
这⾏代码的作⽤是限制LogUtils类⾥的静态代码块初始化,因为LogUtils类在第⼀次调⽤时会加载⼀些本地资源配置,⽐较耗费时间,所以可以使⽤power mock禁⽌初始化
然后在tup()⽅法⾥对两个静态类进⾏mock设置
最后在GetUrByIdStatic测试⽅法⾥对getBirAgeSex()⽅法指定返回默认值:PowerMockito.BirAgeSex(Mockito.any())).thenReturn(idMap)
(power mock的具体⽤法⽹上资料很多,这⾥不展开说明)
运⾏时在控制台会输出:
Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
这是powermock的警告信息,不影响运⾏结果
另外,如果你的单元测试代码不需要对静态⽅法, final⽅法mock, 就没必要使⽤power mock, 使⽤Spock⾃带的Mock()就⾜够了
因为power mock的原理是在编译期通过ASM字节码修改⼯具修改我们的代码,然后使⽤⾃⼰的classLoader加载,加载的静态⽅法越多会相应的增加测试时长
⽂章来源: