如何使用mockito-inline 模拟超类并抑制对实际超类构造函数的调用?

问题描述 投票:0回答:1

我看到了一些关于使用常规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);
    }
}
android mockito junit4
1个回答
0
投票

在实例化子类时模拟超类的创建对我有用: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 中的一个错误,或者你是否应该采取不同的做法。

© www.soinside.com 2019 - 2024. All rights reserved.