我看到了一些关于使用常规mockito模拟制作器的老问题。即使用
mockito-core
依赖项。我认为这不是重复的,因为我使用的是 mockito-inline
中的新内联模拟制作器。它支持构造函数模拟,所以我认为现在这应该是可能的。我正在尝试删除之前使用的 PowerMockito 依赖项,因此此处不能选择使用 PowerMockito。我正在尝试使用 JUnit 4 和 Mockito 5.2 来做到这一点。
我在 Android 应用程序中有一堂课
BitmapCache extends LruCache<Uri, Bitmap>
。当不在 Android 设备上运行时,LruCache
构造函数会抛出一个存根异常,就像 android.os
中的所有方法一样。我的类的构造函数在其构造函数内调用 super(cache_size);
。所以我无法实例化一个实例来测试。我想模拟 LruCache
而不是 BitmapCache
并且我想抑制对 LruCache
构造函数的调用。看来 Mockito 的 mockConstruction
方法仍然调用模拟类构造函数。触发存根异常。
是否可以使用
mockConstruction
、mockConstructionWithAnswer
或使用新的内联模拟生成器的其他功能?我想完全阻止 LruCache
构造函数抛出存根异常。理想情况下,如果可能的话,最好阻止它被调用,或者用空函数将其删除。
我显然无法更改父类中的代码,这确实需要扩展
LruCache
。我认为这将是测试 Android 代码时的一个常见问题,无需启动虚拟机在 Android 设备上运行测试。
我尝试将defaultAnswer设置为所有常量值,包括深度模拟和使用
mockConstructionWithAnswer
,但它们似乎并没有真正模拟构造函数,而是模拟所有其他方法。
异常总是从 try 块内的行抛出,我在其中实例化一个
BitmapCache
对象进行测试。
我也不确定这是否是 LruCache 通用的问题,所以我的模拟看起来像这样强制将
LruCache.class
强制转换为 Class<LruCache<Uri, Bitmap>>
。
try (MockedConstruction<LruCache<Uri, Bitmap>> lruCache =
mockConstruction(
(Class<LruCache<Uri, Bitmap>>) (Class<?>) LruCache.class))
我也尝试过这两种方法,但都没有抑制对 LruCache 构造函数的调用。
将
mockCache
定义为
LruCache<Uri, Bitmap> mockCache =
mock((Class<LruCache<Uri, Bitmap>>) (Class<?>) LruCache.class;
try (MockedConstruction<LruCache<Uri, Bitmap>> lruCache =
mockConstruction(
(Class<LruCache<Uri, Bitmap>>) (Class<?>) LruCache.class,
(mock, context)->{
when(mock).thenReturn(mockCache);
})){
和
try (MockedConstruction<LruCache<Uri, Bitmap>> lruCache =
mockConstructionWithAnswer(
(Class<LruCache<Uri, Bitmap>>) (Class<?>) LruCache.class,
(invocation) -> mockCache)) {
更新:我尝试模拟 BitmapCache 的构造函数,但这似乎并没有调用 LruCache 构造函数。我认为上面的问题要么是 mockConstruction 不适用于
super()
调用,要么是我在调用 mockConstruction
和 mockConstructionWithAnswer
时没有正确指定类或构造。
我倾向于
super()
呼叫未被拦截。我尝试如下模拟这两个类,一旦子类尝试调用父类上的“模拟”方法,它实际上会调用真正的方法并抛出异常。
Bitmap bitmap1 = mock(Bitmap.class);
Bitmap bitmap2 = mock(Bitmap.class);
Uri uri1 = mock(Uri.class);
Uri uri2 = mock(Uri.class);
try (MockedConstruction<LruCache<Uri, Bitmap>> lruCache =
mockConstruction(
(Class<LruCache<Uri, Bitmap>>) (Class<?>) LruCache.class,
(mock, context)->{
// These four calls should prevent calls to the real LruCache
// get or put methods from being called.
doNothing().when(mock.put(any(Uri.class), any(Bitmap.class)));
when(mock.get(uri1)).thenReturn(bitmap1);
when(mock.get(uri2)).thenReturn(bitmap2);
when(mock.get(any(Uri.class))).thenReturn(null);
})) {
try (MockedConstruction<BitmapCache> bitmapCache =
mockConstruction(BitmapCache.class, (mock, context)->{
when(mock.put(any(Uri.class), any(Bitmap.class)))
.thenCallRealMethod();
when(mock.get(any(Uri.class)))
.thenCallRealMethod();
})) {
BitmapLoader loader = mock(BitmapLoader.class);
cache = new BitmapCache(cacheSize, loader);
// This call calls the real BitmapCache method as expected,
// but then that method calls the real LruCache.put method
// instead of the mock.
cache.put(uri1, bitmap1);
cache.put(uri2, bitmap2);
assertEquals(cache.get(uri1), bitmap1);
assertEquals(cache.get(uri2), bitmap2);
}
}
在实例化子类时模拟超类的创建对我有用:mockito-inline v4.6.1,但在使用:mockito-core v5.14.2
时则不然因此,以下 TestNG 单元测试中的断言通过 v4.6.1 并通过 v5.14.2 失败:
public class NewTest {
@Test
public void test() {
try (MockedConstruction<Super> superConstructorMock = Mockito.mockConstruction(Super.class,
(mock, context) -> {
Mockito.when(mock.getInt()).thenReturn(42);
})) {
Child c = new Child();
Assert.assertEquals(c.getInt(), 42);
}
}
class Super {
int getInt() {
return 0;
}
}
class Child extends Super {}
}
Mockito v4 不支持 Java 21,因此它不是一个解决方案。
我不知道这是否是 Mockito 中的一个错误,或者你是否应该采取不同的做法。