You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何Mock抽象类MySession中的protected方法beginTransaction

问题背景

被测类MyClassHelper继承自抽象类MySession,其myMethod()方法调用了父类的protected方法beginTransaction()。MySession是项目依赖包中的类,beginTransaction()位于其他包且为protected权限。

遇到的问题

编写MyClassHelper的单元测试时,无法对beginTransaction()方法进行Mock:

  • 创建同包测试类MySessionTest尝试Mock该方法,但执行时仍会进入MySession原方法逻辑;
  • 在测试类中操作sessionMock时,无法调用beginTransaction()方法(无代码提示)。
相关代码

被测类

package my.main.class.package;

public class MyClassHelper extends MySession {

// Session is an abstract class.

// the method that I want to test
public PrimaryKey myMethod() throws Exception {
    try {
        // this is a method of Session class
        beginTransaction(TXN_NOT_TIMED);

        // doSomething;
    } catch(Exception e) {
        // another method from abstract class.
        rollback();
    }
}

抽象类MySession(依赖包中)

package my.class.some.other.package;

public abstract class MySession implements Session {
    protected void beginTransaction(boolean timedTransaction) throws SessionException {
        this.getTransactionBeginWork((String)null, true, timedTransaction, false);
    }
}

失败的尝试代码

同包测试类

package my.class.some.other.package

class MySessionTest {

    @Mock private MySession session;

    public void beginTransaction() throws SessionException {
        doNothing().when(session).beginTransaction(anyBoolean());
    }
}

测试类

@ExtendWith(MockitoExtension.class)
public class MyClassHelperTest {
    private MySimpleSessionTest sessionTest = new MySimpleSessionTest();
    @InjectMock private MyClassHelper myClassHelper;
    @Mock MySession sessionMock;

    @Test
    public void myTest() {

        // one way
        sessionTest.beginTransaction();
        // what happens here is eventhough I did doNothing() in MySessionTest, it doesn't mock that method and instead execution goes inside beginTransaction() of MySession class.

        doNothing().when(sessionMock). // here I don't get the beginTransaction() option after "."
        myClassHelper.myMethod();
    }
}
可行的Mock方案

方案一:Mockito Spy + 指定方法名Mock

Mockito的spy支持部分Mock真实对象,通过指定方法名的重载when方法,可以绕过权限限制Mock protected方法:

@ExtendWith(MockitoExtension.class)
public class MyClassHelperTest {

    @Spy
    private MyClassHelper myClassHelper = new MyClassHelper();

    @Test
    public void myTest() throws Exception {
        // 直接指定方法名和参数类型,Mockprotected方法的行为
        doNothing().when(myClassHelper, "beginTransaction", anyBoolean());

        myClassHelper.myMethod();

        // 验证方法是否按预期调用
        verify(myClassHelper, times(1)).beginTransaction(TXN_NOT_TIMED);
    }
}

这种方式不需要依赖代码提示,直接通过方法名字符串定位目标方法,适配protected权限的Mock需求。

方案二:自定义子类覆盖protected方法

在测试包中创建MyClassHelper的子类,主动覆盖父类的beginTransaction()方法,直接替换原方法逻辑:

// 测试包内的自定义子类
public class TestableMyClassHelper extends MyClassHelper {
    private boolean beginTransactionCalled = false;

    @Override
    protected void beginTransaction(boolean timedTransaction) throws SessionException {
        // 空实现或记录调用状态,按需调整
        beginTransactionCalled = true;
    }

    public boolean isBeginTransactionCalled() {
        return beginTransactionCalled;
    }
}

测试类中使用该子类:

@ExtendWith(MockitoExtension.class)
public class MyClassHelperTest {

    private TestableMyClassHelper myClassHelper = new TestableMyClassHelper();

    @Test
    public void myTest() throws Exception {
        myClassHelper.myMethod();

        // 验证目标方法是否被触发
        assertTrue(myClassHelper.isBeginTransactionCalled());
    }
}

这种方式无需依赖Mock框架的特殊语法,通过子类覆盖直接控制方法行为,适合简单场景。

方案三:PowerMock突破权限限制(需引入依赖)

如果项目允许使用PowerMock,可以直接Mock父类的protected/private方法:

@RunWith(PowerMockRunner.class)
@PrepareForTest(MySession.class)
public class MyClassHelperTest {

    @InjectMocks
    private MyClassHelper myClassHelper;

    @Test
    public void myTest() throws Exception {
        // Mock父类的protected方法
        PowerMockito.doNothing().when(myClassHelper, "beginTransaction", anyBoolean());

        myClassHelper.myMethod();

        // 验证方法调用次数
        PowerMockito.verifyPrivate(myClassHelper, times(1)).invoke("beginTransaction", TXN_NOT_TIMED);
    }
}

PowerMock可以突破普通Mockito的限制,直接处理父类、protected、private方法,但需要额外引入依赖并配置测试运行器,适合复杂Mock场景。

内容的提问来源于stack exchange,提问作者Abhinash Jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 20:03:15