如何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
相关产品推荐
相关产品推荐

