我有如下验证逻辑
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));
}
}
其中StringFormatValidator
是如下的功能接口
@FunctionalInterface
public interface StringFormatValidator extends Serializable {
boolean apply(String arg);
}
我已经重写了等于以比较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中捕获的值如何释放它的可序列化行为,因为它不是模拟值,而是在调用站点中实际创建的。
注意:我简化了总体签名和命名,以仅将重点放在眼前的问题上。
答案 0 :(得分:0)
花了一些时间后,我发现了问题,我将回答自己的问题,这样可以帮助处于类似情况的人。
我从以下SO帖子中获得了见解:
1. What is the difference between a lambda and a method reference at a runtime level?
2. Equality of instance of functional interface in java
我遇到了两个问题。
首先,上面java.io.NotSerializableException: Non-serializable lambda
的问题中提到的原始问题。我给人的印象是,从Mockito捕获的参数以某种方式干扰并且捕获的lambda参数不再可序列化。但是,这更多地与Java中lambda的序列化通常如何发生有关。
我仍然不完全了解内部原理,但是在其中一种情况下,当您真的不知道起作用的情况时,该异常已解决。
然后,我遇到了相等失败的问题,因为序列化的值包含调用站点。因此,在测试类中创建的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;
}