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

Mockito模拟未调用问题:如何Mock父类downloadHtml方法?

解决子类调用Mock父类方法的问题

问题原因

Mockito 默认无法直接 Mock 父类的非抽象方法。当你直接 Mock 子类对象时,子类继承自父类的 downloadHtml 方法会执行真实逻辑,而非你定义的 Mock 行为,导致 verify 时提示无交互记录。

可行解决方案

1. 使用 Spy 包装真实子类对象

Spy 可以保留对象的真实行为,同时对指定方法进行 Stub,适合需要复用子类真实逻辑但替换父类方法的场景:

@Test
public void testScrape() {
    // 创建子类的 Spy 对象,保留真实逻辑
    TScrapperProvider scrapper = Mockito.spy(new TScrapperProvider());
    String mockHtml = "<html>mock content</html>";
    
    // 用 doReturn 避免触发真实方法执行,Stub 父类的 downloadHtml
    Mockito.doReturn(mockHtml).when(scrapper).downloadHtml(Mockito.anyString());
    
    // 调用子类业务方法
    scrapper.scrape();
    
    // 验证父类方法被调用
    Mockito.verify(scrapper).downloadHtml(Mockito.anyString());
}

注意必须用 doReturn 而非 when(scrapper.downloadHtml(...)),后者会先执行真实方法再 Stub。

2. 重构代码:用依赖替代继承(推荐)

继承关系会将父类逻辑与子类紧耦合,不利于测试。更合理的设计是把父类的下载逻辑抽成独立依赖,让子类通过依赖注入使用:

// 抽离出独立的下载类
public class HtmlDownloader {
    public String downloadHtml(String url) {
        // 原父类 SB 的下载逻辑
        return "real html";
    }
}

// 子类改为依赖注入,不再继承 SB
public class TScrapperProvider implements Scrapper {
    private final HtmlDownloader downloader;

    public TScrapperProvider(HtmlDownloader downloader) {
        this.downloader = downloader;
    }

    @Override
    public void scrape() {
        String html = downloader.downloadHtml("target-url");
        // 后续业务逻辑
    }
}

测试时直接 Mock 依赖类即可,完全避免继承带来的 Mock 问题:

@Test
public void testScrape() {
    HtmlDownloader mockDownloader = Mockito.mock(HtmlDownloader.class);
    String mockHtml = "<html>mock content</html>";
    
    Mockito.when(mockDownloader.downloadHtml(Mockito.anyString())).thenReturn(mockHtml);
    
    TScrapperProvider scrapper = new TScrapperProvider(mockDownloader);
    scrapper.scrape();
    
    Mockito.verify(mockDownloader).downloadHtml(Mockito.anyString());
}

这种设计符合依赖倒置原则,代码更易维护和扩展。

3. 使用 PowerMock 强制 Mock 父类方法(不推荐)

如果无法修改现有代码结构,可以用 PowerMock 绕过类加载机制来 Mock 父类方法,但会引入额外复杂度和兼容性风险:

@RunWith(PowerMockRunner.class)
@PrepareForTest(TScrapperProvider.class)
public class TScrapperProviderTest {
    @Test
    public void testScrape() {
        TScrapperProvider scrapper = Mockito.mock(TScrapperProvider.class);
        String mockHtml = "<html>mock content</html>";
        
        // 让子类的 scrape 方法执行真实逻辑
        Mockito.when(scrapper.scrape()).thenCallRealMethod();
        // Mock 父类的 downloadHtml 方法
        PowerMockito.when(scrapper.downloadHtml(Mockito.anyString())).thenReturn(mockHtml);
        
        scrapper.scrape();
        PowerMockito.verify(scrapper).downloadHtml(Mockito.anyString());
    }
}

需注意:PowerMock 需要额外引入依赖,且可能与部分框架冲突,仅作为最后备选方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 02:17:03