【问题标题】:javax.validation.ValidationException: HV000028: Unexpected exception during isValid in Testjavax.validation.ValidationException: HV000028: isValid 测试期间出现意外异常
【发布时间】:2020-05-30 10:35:45
【问题描述】:

我不知道如何为自定义约束验证器编写单元测试。我有以下 Validator 类:

@Slf4j
@AllArgsConstructor
@NoArgsConstructor
public class SchoolIdValidator implements ConstraintValidator<ValidSchoolId, String> {

    private SchoolService schoolService;

    @Override
    public void initialize(ValidSchoolId constraintAnnotation) {}

    @Override
    public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
        return schoolService.isValidSchoolId(s);
    }
}

Validator 被以下注解使用:

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = SchoolIdValidator.class)
public @interface ValidSchoolId {

    String message() default "Must be a valid school. Found: ${validatedValue}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

}

SchoolService 是一个使用 ApiClient 调用 School Api 的类,ApiClient 是一个具有通用 restTemplate 请求的通用类:

@Service
@AllArgsConstructor
public class SchoolService {

    private ApiClient apiSchoolClient;

    public Boolean isValidSchoolId(String schoolId){
        try {
            ResponseEntity res = apiSchoolClient.get(
                    String.format("/stores/%s", schoolId), Object.class, null);
            return res.getStatusCode().is2xxSuccessful();
        } catch (HttpClientErrorException e) {
            if(e.getStatusCode().value() == 404)
                return false;
            else
                throw new TechnicalException("Can't get response from school Api");
        } catch(RestClientException e) {
            throw new TechnicalException("Can't get response from School Api");
        }
    }
}

最后是学生类:

@Data
public class Student {

    private String id;
    private String firstName;
    private String lastName;
    @ValidSchoolId
    private String schoolId;
}

当我运行应用程序时,它运行良好。但是,它根本不适用于以下 Test 类:

@RunWith(MockitoJUnitRunner.class)
@ExtendWith(SpringExtension.class)
public class SchoolIdValidatorTest {

    Validator validator;

    @InjectMocks
    private SchoolService schoolService;

    @Mock
    private ApiClient apiSchoolClient;

    @Before
    public void setUp() throws Exception {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
        MockitoAnnotations.initMocks(this);
    }


    @Test
    public void isValid_SchoolIdExists_ShouldValidate() {

        Mockito.when(this.apiSchoolClient.get(Mockito.eq("/schools/12"), Mockito.any(), Mockito.any()))
                .thenReturn(new ResponseEntity<>(HttpStatus.OK));
        //given
        Student student = new Student();
        simulation.setSchoolId("12");
        //when
        Set<ConstraintViolation<Student>> violations = validator.validate(student);

        //then
        assertEquals(0, violations.size());
    }

当它出现在SchoolIdValidator 时,schoolService 总是null。因此,它总是在 SchoolIdValidator.isValid 上抛出 NPE。如果有人有解决方案,那将非常有帮助。

感谢您的阅读和帮助。

【问题讨论】:

  • siteService 不存在。你错过了什么吗?
  • 对不起,这是一个错误。我更改了课程的名称,因为它是一个公司应用程序;)

标签: java spring-boot junit mockito javax.validation


【解决方案1】:

这里的@InjectMocks 可能是问题所在。

@RunWith(MockitoJUnitRunner.class)
@ExtendWith(SpringExtension.class)
public class SchoolIdValidatorTest {

    Validator validator;

    @InjectMocks
    private SchoolService schoolService;

随着

Validation.buildDefaultValidatorFactory().getValidator()

我假设 SchoolService 通常是 Spring 托管的 bean,SchoolIdValidator 会注入它。通过使用@InjectMocks,您是在说这不是 Spring 托管 bean。这个类是由 Mockito 实例化的,所以它永远不会被注入到你的验证器中。

我不确定这里的正确解决方案是什么,因为您没有发布任何配置类。很可能,您需要一个额外的测试配置类来实例化您的验证器ApiClient 并注入您的SchoolService。您也可以只模拟 ApiClient@MockBean,这将使其成为 Spring 托管 bean,但也允许模拟。

有点相关,这里确实有构造函数。没有理由使用@InjectMocks。只需使用new SchoolService(apiSchoolClient)

【讨论】:

  • 感谢您的回复。我试图用@Mock 注释apiSchoolClient ,用@MockBean 注释schoolService,但它仍然是null
  • 你不需要模拟schoolService,只需模拟apiSchoolClient。但是,如果您仍然看到 schoolService 作为模拟 bean 的错误,那么您的验证器不是 Spring 托管 bean。您可能需要使用SpringConstraintValidatorFactory。见stackoverflow.com/a/37975083/3059385
  • 很可能是正确的路径,我没有看到任何配置导致SchoolIdValidator 中的schoolService 不是null
猜你喜欢
  • 2021-11-03
  • 1970-01-01
  • 1970-01-01
  • 2015-07-19
  • 2017-07-17
  • 1970-01-01
  • 2021-07-27
  • 2017-08-06
  • 1970-01-01
相关资源
最近更新 更多