Bean验证:如何手动创建ConstraintViolation?

ode*_*dia 5 java validation hibernate-validator bean-validation

我有一个特定的场景,在该场景中,稍后只能手动检查违规情况。

我想做的是抛出一个ConstraintViolationException,并为其提供“真实” ConstraintViolation object(当我将异常捕获到堆栈中时,我使用#{validatedValue}violation.getPropertyPath()参数)。

如何在ConstraintViolation没有框架的情况下通过注释为我创建自己(我使用Hibernate Validator)?

代码示例:

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?...>);
}
Run Code Online (Sandbox Code Playgroud)

谢谢。

Ste*_*erl 6

我不喜欢 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;

}
Run Code Online (Sandbox Code Playgroud)

HTH


Mor*_*fic 5

在我看来,最简单的方法是模拟您的服务以在测试中抛出约束违规。例如,您可以通过扩展类来手动完成此操作,也可以使用模拟框架(例如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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的回答。我想我应该说清楚:这不是为了测试目的,而是生产代码。 (2认同)

Har*_*rdy 5

这里有几件事:

  1. ConstraintViolation是一个接口,因此您可以实现自己的版本

  2. Hibernate Validator 使用该接口自己的内部实现 - org.hibernate.validator.internal.engine.ConstraintViolationImpl。它是一个公共类,但由于它位于内部包中,因此不鼓励您直接使用它。但是,您可能会了解实现ConstraintViolation需要什么。