我们平时写单元测试时经常会遇到调用抽象类或父类的方法,这些抽象方法可能是调用底层接口或数据库,需要mock掉,让抽象方法返回一个我们指定的值,以便测试当前代码逻辑的场景。
下面讲下Spock如何结合power mock实现动态mock抽象方法
一. 抽象方法或父类方法动态Mock
AbstractService
是个抽象类,我们需要把它的方法 parentMethod
模拟掉,返回我们预先设置的"期望值"
代码示例:
public abstract class AbstractService {
String parentMethod(){
// 发起接口调用或数据库操作
return "parentMethod value";
}
}
SubService
是继承AbstractService
的子类,在doSomething
方法体里会调用抽象类的 parentMethod
方法逻辑,然后根据抽象方法 parentMethod
的返回值走不同的逻辑:
public class SubService extends AbstractService {
@Autowired
MoneyDAO moneyDAO;
public String doSomething() {
String parent = super.parentMethod(); // 调用抽象类或父类方法
if ("parent1".equals(parent)) {
// 执行parent1分支逻辑
return "sub1";
}
if ("parent2".equals(parent)) {
// 执行parent2分支逻辑
return "sub2";
}
if ("parent3".equals(parent)) {
// 执行parent3分支逻辑
return "sub3";
}
return "other";
}
}
如果要mock掉抽象类AbstractService
中的 parentMethod
方法, 并且每次mock的值不一样, 可以使用spock + powermock来实现:
单元测试代码如下:
/**
* 测试抽象类方法或父类方法
* @Author: www.javakk.com
* @Description: 公众号:Java老K
* @Date: Created in 14:53 2020/10/05
* @Modified By:
*/
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest([SubService.class])
class AbstractServiceTest extends Specification {
@Unroll
def "测试抽象方法"() {
given: "mock抽象类方法"
def sub = PowerMockito.mock(SubService)
PowerMockito.when(sub.parentMethod()).thenReturn(parentValue) // mock掉抽象类的parentMethod, 返回动态mock值:mockParentReturn
PowerMockito.when(sub.doSomething()).thenCallRealMethod()
expect: "调用doSomething方法"
sub.doSomething() == result
where: "验证分支场景"
parentValue | result
"parent1" | "sub1"
"parent2" | "sub2"
"parent3" | "sub3"
"parent4" | "other"
}
}
使用power mock模拟掉抽象类的方法,返回一个变量parentValue
,然后再放在Spock的where标签里,即可实现动态mock的效果,即每次调用返回的mock值都不一样:parent1
、parent2
、parent3
...
二. 抽象方法+实例方法的动态Mock
如果在SubService
中还有引用其他实例对象的方法,比如下面的业务代码:
public class SubService extends AbstractService {
@Autowired
MoneyDAO moneyDAO; // 金额换算对象
public String doSomethingAndDao() {
String parent = super.parentMethod(); // 调用抽象类或父类方法
BigDecimal money = moneyDAO.getExchangeByCountry(parent); // 获取对应国家的金额
if ("parent1".equals(parent)) {
return money + " CNY";
}
if ("parent2".equals(parent)) {
return money + " USD";
}
if ("parent3".equals(parent)) {
return money + " EUR";
}
return money.toString();
}
}
如果即要mock掉抽象类AbstractService
中的 parentMethod
方法,又要mockmoneyDAO
对象,可以使用 Whitebox.setInternalState
方式
单元测试代码如下:
/**
* 测试抽象类方法或父类方法
* @Author: www.javakk.com
* @Description: 公众号:Java老K
* @Date: Created in 14:53 2020/10/05
* @Modified By:
*/
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(Sputnik.class)
@PrepareForTest([SubService.class])
class AbstractServiceTest extends Specification {
@Unroll
def "测试抽象方法和实例方法"() {
given: "mock抽象类方法"
def sub = PowerMockito.mock(SubService)
// mock掉抽象类的parentMethod, 返回动态mock值:mockParentReturn
PowerMockito.when(sub.parentMethod()).thenReturn(parentValue)
PowerMockito.when(sub.doSomethingAndDao()).thenCallRealMethod()
def moneyDAO = Mock(MoneyDAO)
//将Spockmock的对象moneyDAO使用powermock赋值给SubService的引用moneyDAO
Whitebox.setInternalState(sub, "moneyDAO", moneyDAO)
moneyDAO.getExchangeByCountry(_) >> money // 这样就可以使用spock的动态mock
expect: "调用doSomething方法"
sub.doSomethingAndDao() == result
where: "验证分支场景"
parentValue | money || result
"parent1" | 100 || "100 CNY"
"parent2" | 200 || "200 USD"
"parent3" | 300 || "300 EUR"
"parent4" | 400 || "400"
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。