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

使用JUnit编写单元测试时如何处理protected方法?

先把核心概念给你讲明白

1. 单元测试

就是针对单个代码单元(比如一个方法、一个类)的独立测试,目标是验证这个单元的逻辑是否符合预期,测试时要尽量把外部依赖(比如数据库、其他复杂服务)隔离开,只测当前单元的逻辑。

2. Mock对象

是用来替代真实对象的“假对象”,你可以随便指定它的方法返回什么、被调用了几次,目的就是隔离依赖——比如你测deletePerson时,不需要真的去数据库查人,用Mock的findOrThrow返回你预设的对象就行,这样测试不会受数据库状态影响。

3. 控制内部调用

当被测方法内部调用了其他方法(比如你这里的父类findOrThrow),如果这些调用依赖外部资源或者会干扰测试,你就需要控制它们的行为:要么让它返回预设值,要么跳过真实逻辑,这样才能专注测deletePerson本身的逻辑。

针对你的场景的具体解决方案

你的问题卡在父类的protected方法无法直接Mock/控制,这里给你两种简单可行的办法,都是新手能快速上手的:

方案1:写个测试专用子类(零额外依赖,最推荐)

因为protected方法在子类里是可以访问和重写的,所以我们可以创建一个MyServiceImpl的测试子类,重写findOrThrow方法,让它返回我们想要的对象:

// 在你的测试类里定义这个内部子类
class TestableMyServiceImpl extends MyServiceImpl {
    private Person mockPerson;

    public void setMockPerson(Person mockPerson) {
        this.mockPerson = mockPerson;
    }

    // 重写父类的protected方法,直接返回预设值
    @Override
    protected Person findOrThrow(Long personId) {
        return mockPerson;
    }
}

然后在测试方法里用这个子类:

@Test
void testDeletePerson() {
    // 1. 创建测试子类实例
    TestableMyServiceImpl service = new TestableMyServiceImpl();
    // 2. 预设findOrThrow要返回的Person对象
    Person testPerson = new Person(1L, "张三");
    service.setMockPerson(testPerson);

    // 3. 调用要测的deletePerson方法
    service.deletePerson(1L);

    // 4. 验证逻辑(比如如果deletePerson会调用DAO的delete方法,你可以Mock这个DAO并验证它被调用了)
    // 示例:Mockito.verify(dao).delete(1L);
}

这个方法完全不用额外框架,逻辑直白,新手一眼就能懂,完美绕开了protected方法的访问限制。

方案2:Mockito Spy + 反射(适合不想重写方法的场景)

如果你不想重写整个findOrThrow,只是想临时控制它的返回值,可以用Mockito的Spy配合反射绕过protected的访问限制:

@Test
void testDeletePersonWithSpy() throws Exception {
    // 1. 创建Spy对象:Spy会保留原对象的大部分逻辑,只修改你指定的方法
    MyServiceImpl service = Mockito.spy(new MyServiceImpl());

    // 2. 用反射获取父类的findOrThrow方法,取消访问限制
    Method findOrThrowMethod = MyServiceBase.class.getDeclaredMethod("findOrThrow", Long.class);
    findOrThrowMethod.setAccessible(true);

    // 3. 指定调用findOrThrow(1L)时返回预设的Person
    Person testPerson = new Person(1L, "张三");
    // 注意要用doReturn,不能用when(...).thenReturn(...),后者会触发真实方法调用
    Mockito.doReturn(testPerson).when(service).findOrThrow(1L);

    // 4. 调用被测方法
    service.deletePerson(1L);

    // 5. 验证逻辑
    // 示例:Mockito.verify(dao).delete(1L);
}
为啥你之前的尝试失败?
  • 普通Mockito Spy不能直接拦截父类的protected方法,编译器会因为访问权限报错,因为测试类既不在父类的包下,也不是它的子类,没法直接引用这个方法。
  • 用when(...).thenReturn(...)的话,会先调用真实的findOrThrow方法,这可能会触发你不想看到的逻辑(比如查数据库),甚至导致错误,而doReturn不会触发真实调用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 17:43:12