Mockito 的具体化模拟方法将模拟键入为超类而不是泛型类型

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

我想提供一个更简单的接口(实用方法)来模拟我们在许多测试中经常需要模拟的类的构造,因为它是由第三方库构造的。该类是泛型类型,并且有一个返回其泛型的方法 (

ClassIWantMockedConstructor<T> { whatever(): T }
)。实用方法是这样的:

public interface Execution<T extends ParentClass> {
    void execute(T mock) throws Exception;
}
    
public static <T extends ParentClass> void withMockedConstruction(Execution<T> execution) {
    T mock = mock();
    try (@SuppressWarnings("unused") MockedConstruction<?> construction = mockConstruction(
            ClassIWantMockedConstructor.class,
            (mocked, ctx) -> when(mocked.whatever()).thenReturn(mock)
    )) {
        execution.execute(mock);
    } catch (Throwable t) {
        throw new RuntimeException("Unexpected error during tests, please check the tests", t);
    }
}

但是,如果我使用

withMockedConstruction
传递带有 ParentClass 子类类型的 lambda,则
T
的类型仍为
ParentClass
。这会导致稍后出现
ClassCastException
。示例:

// When called like this
withMockedConstruction((ChildClass mockService) -> { ... });

// Then debugging
public static <T extends ParentClass> void withMockedConstruction(Execution<T> execution) {
    T mock = mock(); // mock = Mock for ParentClass, T reified as ParentClass
    ...
        execution.execute(mock); // throws ClassCastException
}

我可以克服这个问题的唯一方法是手动为模拟指定一个类,从而更改签名以需要该类,如下所示:

// Change signature to this:
public static <T extends ParentClass> void withMockedConstruction(Class<T> clazz, Execution<T> execution) {
    T mock = mock(clazz); // mock = Mock for ChildClass normally
    ...
}

// Calling like this, works as intended
withMockedConstruction(ChildClass.class, mockService -> { ... });

有没有办法只使用refied来达到这个结果? 我可以强制它以某种方式检查 lambda 函数的引用类型吗? 在这种情况下使用类是最好的解决方案吗?

java unit-testing junit mockito junit5
1个回答
0
投票

用于具体化泛型的“技巧”不能传递地起作用:你不能自己使用泛型并期望该技巧起作用。

public static <T extends ParentClass> void withMockedConstruction(
      Execution<T> execution) {
    T mock = mock();

在运行时,

T
被擦除为
ParentClass
,因此
mock()
将具体化
ParentClass
,而不是
T
。 (我想这意味着这是一个特定的情况,其中
@SafeVarargs
实际上是错误的。)

您可以也许识别 lambda 的参数类型,可能使用带有默认方法的自定义功能接口,采用像thisone这样的技巧,但此时与简单地传递

Class
,因为您必须始终使用括号指定 lambda 参数类型。

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