我有一个特定的场景,我只能在流程的后期手动检查违规情况。
我想要做的是抛出一个ConstraintViolationException
,并提供一个真实的" ConstraintViolation object
它(当我在堆栈中捕获异常时,我使用#{validatedValue}
和violation.getPropertyPath()
参数)。
如何在没有框架通过注释(我使用Hibernate Validator)为我做的情况下自己创建ConstraintViolation
?
代码示例:
List<String> columnsListForSorting = new ArrayList<String>(service.getColumnsList(domain));
Collections.sort(columnsListForSorting);
String firstFieldToSortBy = this.getTranslatedFieldName(domain.getClass().getCanonicalName(), sortingInfo.getSortedColumn());
if (!columnsListForSorting.contains(firstFieldToSortBy)){
throw new ConstraintViolationException(<what here?...>);
}
感谢。
答案 0 :(得分:3)
这里有几件事:
ConstraintViolation 是一个界面,因此您可以实现自己的版本
Hibernate Validator使用自己的内部实现 - org.hibernate.validator.internal.engine.ConstraintViolationImpl 。它是一个公共类,但由于它位于内部包中,因此不鼓励您直接使用它。但是,您可能知道实现 ConstraintViolation 所需的内容。
答案 1 :(得分:3)
我不喜欢Hibernate Validator的另一个原因。他们让非常难以编程方式创建一个简单的违规行为,当它应该是简单的。我有测试代码,我需要创建一个违规来提供给我的模拟子系统。
无论如何,还没有推出你自己的违规禁令实施 - 这就是我为一个字段创建违规行为的方法:
private static final String MESSAGE_TEMPLATE = "{messageTemplate}";
private static final String MESSAGE = "message";
public static <T, A extends Annotation> ConstraintViolation<T> forField(
final T rootBean,
final Class<T> clazz,
final Class<A> annotationClazz,
final Object leafBean,
final String field,
final Object offendingValue) {
ConstraintViolation<T> violation = null;
try {
Field member = clazz.getDeclaredField(field);
A annotation = member.getAnnotation(annotationClazz);
ConstraintDescriptor<A> descriptor = new ConstraintDescriptorImpl<>(
new ConstraintHelper(),
member,
annotation,
ElementType.FIELD);
Path p = PathImpl.createPathFromString(field);
violation = ConstraintViolationImpl.forBeanValidation(
MESSAGE_TEMPLATE,
MESSAGE,
clazz,
rootBean,
leafBean,
offendingValue,
p,
descriptor,
ElementType.FIELD);
} catch (NoSuchFieldException ignore) {}
return violation;
}
HTH
答案 2 :(得分:1)
在我看来,最简单的方法是将您的服务模拟为在测试中抛出约束违规。例如,您可以通过扩展类来手动执行此操作,也可以使用mockito之类的模拟框架。我更喜欢模拟框架,因为它们简化了很多事情,因为你既不需要创建和维护其他类,也不必处理在测试对象中注入它们。
以mockito为起点,你可能会写一些类似于:
的东西import org.hibernate.exception.ConstraintViolationException;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.mockito.Mockito.when;
public class MyTest {
@Mock /* service mock */
private MyService myService;
@InjectMocks /* inject the mocks in the object under test */
private ServiceCaller serviceCaller;
@Test
public void shouldHandleConstraintViolation() {
// make the mock throw the exception when called
when(myService.someMethod(...)).thenThrow(new ConstraintViolationException(...))
// get the operation result
MyResult result = serviceCaller.doSomeStuffWhichInvokesTheServiceMethodThrowingConstraintViolation();
// verify all went according to plan
assertWhatever(result);
}
}
答案 3 :(得分:0)
为什么不在测试中注入Validator
并创建一个触发所需验证错误的对象?
Set<ConstraintViolation<T>> res = validator.validate(object);
答案 4 :(得分:0)
我已经多次遇到此问题,并提出了这个小解决方案。
它基于Validator的validateValue方法,并在测试类中使用辅助验证器,该工具用于在一个衬里中生成Set<ConstraintViolation<T>>
对象。
假设您正在测试一个类,该类内部使用在我们的测试中模拟的Validator,该对象在具有Bean Validation批注的对象“ Data data”上使用,例如,字段“ sessionId”用@NotNull批注。
您的测试课程将至少具有以下两个成员:
@Mock
private Validator validator;
private Validator violationCreatorValidator;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
violationsGenerator.Validation.buildDefaultValidatorFactory().getValidator();
}
然后在测试用例中:
when(validator.validate(data))
.thenReturn(violationsGenerator.validateValue(Data.class, "sessionId", null));
就这样。无需实现ConstraintViolation接口或调用巨大的ConstraintViolationImpl.forBeanValidation。让验证器为您完成这项工作。