如何测试含动态数据的类?final依赖对象的单元测试方案
解决final成员变量ParserBets的测试mock问题
针对你要测试PageBets.refreshEvents()、需要控制parser.getEvents()行为的场景,不需要写代理,有几个更简洁的方案:
1. 重构构造器,支持依赖注入(推荐)
当前代码的问题在于构造器内部硬编码调用initParser()初始化final的parser,导致测试时无法替换。最干净的做法是重构构造器,把parser作为参数传入:
// 保留原有的生产用构造器,兼容现有代码 public PageBets(String uri) throws MalformedURLException, URISyntaxException { this(initParser()); try { // 原有的uri相关初始化逻辑... this.events = refreshEvents(); } catch (URISyntaxException | MalformedURLException e) { log.debug("Not correct format link: {}", uri, e); throw e; } } // 新增供测试使用的构造器(可以设为包私有,或用@VisibleForTesting注解标记) PageBets(ParserBets parser) { this.parser = parser; this.events = new TreeSet<>(); // 初始化空集合,或者根据测试需求调整 }
测试时直接传入mock的ParserBets实例即可,完全不受成员变量final的限制:
// 假设用Mockito创建mock ParserBets mockParser = Mockito.mock(ParserBets.class); // 预设getEvents的返回值 when(mockParser.getEvents(any(PageBets.class))).thenReturn(new TreeSet<>(/* 测试用Event集合 */)); // 创建测试用的PageBets实例 PageBets testPage = new PageBets(mockParser); // 调用refreshEvents并验证逻辑 TreeSet<Event> result = testPage.refreshEvents(); // 断言结果符合预期
2. 使用Mocking框架直接mock final类(如果ParserBets是final类)
如果ParserBets本身是final类,Mockito 2.x及以上版本支持mock final类,只需简单配置:
- 在
src/test/resources/mockito-extensions目录下创建文件org.mockito.plugins.MockMaker - 文件内容写入:
mock-maker-inline
之后就可以直接mock final的ParserBets类,配合构造器注入使用,步骤和上面一致。
3. 静态方法mock(不推荐,仅当无法重构原代码时用)
如果完全不能修改PageBets的构造器,可以用PowerMock来mockinitParser()静态方法,让它返回mock的ParserBets:
@RunWith(PowerMockRunner.class) @PrepareForTest(PageBets.class) public class PageBetsTest { @Test public void testRefreshEvents() throws Exception { ParserBets mockParser = Mockito.mock(ParserBets.class); when(mockParser.getEvents(any(PageBets.class))).thenReturn(/* 测试数据 */); // mock initParser方法返回mock对象 PowerMockito.mockStatic(PageBets.class); when(PageBets.initParser()).thenReturn(mockParser); // 创建PageBets实例 PageBets testPage = new PageBets("test-uri"); // 执行测试并验证 } }
这种方式会增加测试复杂度,且PowerMock可能和其他测试框架存在兼容性问题,优先推荐前两种方案。
内容的提问来源于stack exchange,提问作者Danila
相关产品推荐
相关产品推荐

