相关文章推荐
内向的课本  ·  python - Convert a ...·  1 年前    · 
酷酷的开心果  ·  c++ - How do you ...·  1 年前    · 

我们平时写单元测试时经常会遇到调用抽象类或父类的方法,这些抽象方法可能是调用底层接口或数据库,需要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"

关于动态mock的更多用法可以参考这篇文章:Spock高级用法 - 动态mock

(完整的代码已上传到github,在公众号里回复spock即可获取github项目地址)

-END-