如何用Mockito-inline模拟父类并阻止调用其真实构造方法?
问题背景与解决方案
核心问题
使用JUnit 4 + Mockito 5.2(依赖mockito-inline的内联MockMaker)移除PowerMockito依赖时,遇到Android类LruCache的构造方法在非Android环境下抛出Stub异常的问题:项目中BitmapCache extends LruCache<Uri, Bitmap>,子类构造方法调用super(cache_size)触发异常,需要模拟LruCache以避免调用真实构造,但mockConstruction无法拦截子类的super()调用。
关键原因
Mockito的mockConstruction仅能拦截直接实例化目标类的场景(如new LruCache(...)),子类构造中的super()属于类内部的父类构造调用,不会被拦截,因此仍会触发真实LruCache构造的异常。
可行解决方案
方案:直接模拟BitmapCache并覆盖父类方法行为
不需要单独模拟LruCache,直接通过mockConstruction创建BitmapCache的mock对象,既避免调用真实构造(包括父类super()),又能测试子类自身的业务逻辑,同时覆盖父类方法的行为:
@Test public void testBitmapCache() { Bitmap bitmap1 = mock(Bitmap.class); Bitmap bitmap2 = mock(Bitmap.class); Uri uri1 = mock(Uri.class); Uri uri2 = mock(Uri.class); BitmapLoader loader = mock(BitmapLoader.class); int cacheSize = 1024; try (MockedConstruction<BitmapCache> mockedCache = mockConstruction(BitmapCache.class, (mock, context) -> { // 让BitmapCache的get/put调用真实方法,测试子类自身逻辑 when(mock.put(any(Uri.class), any(Bitmap.class))).thenCallRealMethod(); when(mock.get(any(Uri.class))).thenCallRealMethod(); // 覆盖父类LruCache的方法行为,避免触发真实逻辑 when(mock.get(uri1)).thenReturn(bitmap1); when(mock.get(uri2)).thenReturn(bitmap2); when(mock.get(any(Uri.class))).thenReturn(null); doNothing().when(mock).put(any(Uri.class), any(Bitmap.class)); })) { // 创建mock实例,不会调用真实构造(包括super()) BitmapCache cache = new BitmapCache(cacheSize, loader); // 执行测试逻辑 cache.put(uri1, bitmap1); cache.put(uri2, bitmap2); assertEquals(bitmap1, cache.get(uri1)); assertEquals(bitmap2, cache.get(uri2)); } }
方案说明
- 避免真实构造调用:
mockConstruction(BitmapCache.class)创建的mock对象不会执行真实的构造方法(包括父类super()),彻底规避Stub异常。 - 保留子类逻辑测试:通过
thenCallRealMethod()让BitmapCache的get/put调用真实方法,确保测试子类自身的业务逻辑。 - 覆盖父类方法:直接给mock对象的父类方法设置预期行为,当子类真实方法调用
super.get()/super.put()时,会触发mock逻辑而非真实LruCache的方法。
为什么之前的尝试无效?
- 模拟
LruCache无法拦截子类super()调用:mockConstruction不处理类内部的父类构造调用,因此仍会触发真实LruCache构造。 - 同时模拟两个类导致冲突:
BitmapCache的mock已包含父类LruCache的mock行为,单独模拟LruCache不会影响子类mock的父类方法调用。
内容的提问来源于stack exchange,提问作者majinnaibu
相关产品推荐
相关产品推荐

