java - 从 Mockito 参数捕获器捕获时功能接口不可序列化
问题描述
我有一些验证逻辑如下
public interface IValidation {
void validate();
}
public class ParameterValidator {
public void validate(IValidation... validations) {
for (IValidation validation : validations) {
validation.validate();
}
}
}
验证之一是在 StringFormat 上,如下所示
public class StringFormatValidation implements IValidation {
public StringFormatValidation(StringFormatValidator stringFormatValidator, String param) {
...
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof StringFormatValidation)) return false;
StringFormatValidation other = (StringFormatValidation) obj;
if (!Objects.equals(this.param, other.param)) return false;
return
Arrays.equals(SerializationUtils.serialize(this.stringFormatValidator),
SerializationUtils.serialize(other.stringFormatValidator));
}
}
whereStringFormatValidator
是一个功能接口如下
@FunctionalInterface
public interface StringFormatValidator extends Serializable {
boolean apply(String arg);
}
我已经覆盖了equals来比较序列化字节上的lambda(到目前为止还不确定其他更好的方法)。我有一个按预期工作的以下单元测试
@Test
public void testEquality() {
StringFormatValidation testFormatValidation1 = new
StringFormatValidation(StringFormatValidators::isCommaSeparated,"test1");
StringFormatValidation testFormatValidation2 = new
StringFormatValidation(StringFormatValidators::isCommaSeparated,"test2");;
Assert.assertEquals(testFormatValidation1, testFormatValidation2);
}
但是当我尝试如下测试调用站点时,
@MockBean
ParameterValidator parameterValidator;
@Captor
ArgumentCaptor<IValidation> argumentCaptor;
@Test
public void testParameterValidations() {
testResource.doSomething(parameter1, "testParam");
Mockito.verify(parameterValidator).validate(argumentCaptor.capture());
List<IValidation> actualValidationList = argumentCaptor.getAllValues();
StringFormatValidation testFormatValidation = new
StringFormatValidation(StringFormatValidators::isCommaSeparated,
"testParam");
Assert.assertTrue(actualValidationList.contains(testFormatValidation));
}
我得到参数捕获器java.io.NotSerializableException: Non-serializable lambda
中的值的异常。StringFormatValidation
我不明白 Mockito 的参数 caprtor 中捕获的值如何失去它的可序列化行为,因为它不是模拟值,而是实际在调用站点中创建的。
注意:我已经简化了整体签名和命名,以便只关注手头的问题。
解决方案
花了一些时间后,我发现了问题,我会回答我自己的问题,以便它可以帮助处于类似情况的人。我从以下 SO 帖子中获得了见解: 1. lambda 和运行时级别的方法引用有什么区别?
2. java中函数接口实例的相等性
我遇到了两个问题。一、上面问题中提到的原问题java.io.NotSerializableException: Non-serializable lambda
. 我的印象是,来自 Mockito 的参数捕获会以某种方式干扰,并且捕获的 lambda 参数不再是可序列化的。然而,它更多地与 java 中 lambda 的序列化一般如何发生有关。我仍然不完全了解内部结构,但是当您真的不知道什么有效时,在其中一种情况下解决了异常。然后我遇到equals失败,因为序列化值包含调用站点。因此StringFormatValidation testFormatValidation1 = new
StringFormatValidation(StringFormatValidators::isCommaSeparated,"test1");
,在测试类中创建的将具有测试类的路径,而主类中的相同构造将具有它的路径。我通过提取StringFormatValidators::isCommaSeparated
静态变量并从所有呼叫站点使用它来解决这个问题。
public class StringFormatValidators {
private static boolean isCommaSeparatedFn(String arg) {
String COMMA_SEPARATED_STRINGS = "^[a-zA-Z0-9]+[a-zA-Z0-9-_:]*(,[a-zA-Z0-9]+
[a-zA-Z0-9-_:]*)*$";
Pattern COMMA_SEPARATED_STRINGS_PATTERN =
Pattern.compile(COMMA_SEPARATED_STRINGS);
return arg != null && COMMA_SEPARATED_STRINGS_PATTERN.matcher(arg).find();
}
public static final StringFormatValidator isCommaSeparated =
StringFormatValidators::isCommaSeparatedFn;
}
推荐阅读
- epplus - EPPlus 4.5.3.3 版本的最后一次提交是什么?
- batch-file - 如何使用正在进行的 INPUT/OUTPUT 运行 .exe/.bat 文件 4GL
- java - 比较存储在两个不同数组中的字符串,如果字符串匹配,则从另一个数组输出字符串
- javascript - R/ShinyApp: conditionalPanel - 测试文件是否已上传
- html - PDF分页符背景颜色
- ios - 如何在 swift 框架中使用 cocoapods
- reactjs - Electron+React 深度链接
- tinymce - 在 TinyMCE 中,上传图像的名称会根据您使用拖放还是文件上传而有所不同。我们可以保留原来的名字吗?
- android - 如何在颤振应用程序中旋转文本?
- .net-core - Outlook Redemption 是否支持 .Net 核心?