我想提供一个更简单的接口(实用方法)来模拟我们在许多测试中经常需要模拟的类的构造,因为它是由第三方库构造的。该类是泛型类型,并且有一个返回其泛型的方法 (
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 函数的引用类型吗? 在这种情况下使用类是最好的解决方案吗?
用于具体化泛型的“技巧”不能传递地起作用:你不能自己使用泛型并期望该技巧起作用。
public static <T extends ParentClass> void withMockedConstruction(
Execution<T> execution) {
T mock = mock();
在运行时,
T
被擦除为ParentClass
,因此mock()
将具体化ParentClass
,而不是T
。 (我想这意味着这是一个特定的情况,其中 @SafeVarargs
实际上是错误的。)
您可以也许识别 lambda 的参数类型,可能使用带有默认方法的自定义功能接口,采用像thisone这样的技巧,但此时与简单地传递
Class
,因为您必须始终使用括号指定 lambda 参数类型。