Java 自定义约束验证器在单元测试中失败,并显示“未设置目标验证器”错误

Java custom constraints validator fails in unit tests with "No target Validator set" error

提问人:developer747 提问时间:10/13/2023 最后编辑:Olivierdeveloper747 更新时间:11/18/2023 访问量:143

问:

赏金将在 2 天后到期。这个问题的答案有资格获得 +150 声望赏金。developer747 希望引起人们对这个问题的更多关注

我有springboot应用程序,它像这样连接validatorFactoryBean

private LocalValidatorFactoryBean validatorFactoryBean;

@Autowired
public void setValidatorFactoryBean(LocalValidatorFactoryBean validatorFactoryBean) {
    this.validatorFactoryBean = validatorFactoryBean;
}

我在类 Car 上定义了自定义验证器,我像这样运行验证。

 Set<ConstraintViolation<Car>> constraintViolations = validatorFactoryBean.validate(myCar);

所有这些都很好。但是当我尝试通过单元测试来测试它时,在线

validatorFactoryBean.validate(myCar)

我收到错误“未设置目标验证器”。

这就是我在testng单元测试中设置validatorFactoryBean的方式

@InjectMocks
LocalValidatorFactoryBean validatorFactoryBean;

我通过调用 setValidatorFactoryBean setter 将其传递给我正在测试的类。

在调试时,我意识到这个 validatorFactoryBean 并不知道主代码中的所有 ConstraintValidator 实现。

如何使 validatorFactoryBean 了解所有 ConstraintValidator 实现?

java spring-boot mockito testng javax.validation

评论

0赞 BarbetNL 10/24/2023
你是如何开始测试的。你提到了单元测试,但你希望有 spring beans。它是@SpringBootTest,还是通过弹簧扩展扩展,还是仅进行单元测试?我的第一印象是,当您运行测试时,不会创建 bean。为了获得更多帮助,我需要了解您如何创建验证器,您的 Car 类如何使用它,以及您如何设置测试。另外,您是否检查了验证器是否覆盖了“supports”方法以支持类“Car”?
0赞 developer747 11/15/2023
不是 SpringBootTest
0赞 Toni 11/15/2023
@developer747 你能提供测试类或MRE吗?
0赞 BarbetNL 11/15/2023
如果它不是 SpringBootTest,那么您如何运行测试?它需要一个 Bean 进行验证。如果使用 SpringExtension,则需要某种 Spring 上下文配置。如果你使用普通的单元测试,bean将永远不会存在,你需要创建一个新的Validator对象,自动连接将永远不会起作用。

答:

1赞 Ken Chan 11/18/2023 #1

@InjectMocks只是一个嘲讽的东西,从来不懂春天。这就像您手动创建一个没有任何弹簧集成的一样。LocalValidatorFactoryBean

要正确配置,您可以通过使用 to 自动配置 bean 验证的东西来做到这一点。LocalValidatorFactoryBean@SpringBootTest@ImportAutoConfiguration

@SpringBootTest
public class ValidationTest{


    @Configuration
    @ImportAutoConfiguration(ValidationAutoConfiguration.class)                
    @ComponentScan(basePackages = { "my.app.root.package" })
    public static class Config {

    }

    @Autowired
    LocalValidatorFactoryBean validatorFactoryBean;
    

    @Test
    void test(){
    
    }
    
}
1赞 devatherock 11/18/2023 #2

我还建议你使用 ,如 Ken 的回答中所述。但是,如果出于某种原因您不想这样做,那么只要所有自定义约束验证器都具有默认的 no-args 构造函数,使用以下代码初始化的对象就应该适用于测试:@SpringBootTestLocalValidatorFactoryBean

LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
0赞 g00glen00b 11/22/2023 #3

(选项 1)如果 是由 Spring 容器创建的,则在 afterPropertiesSet() 阶段执行一些额外的逻辑。LocalValidatorFactoryBean

您可以通过自己调用此方法来复制此方法。例如:

@BeforeEach
void setUp() {
    validator = new LocalValidatorFactoryBean();
    validator.afterPropertiesSet();
}

(选项 2)但是,这是一种黑客攻击,因为Spring Boot创建的默认值包含额外的定制器和消息插值器。如果您不关心这一点,那么使用底层 JSR-380 bean 验证器可能会更干净。例如:LocalValidatorFactoryBean

@BeforeEach
void setUp() {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    validator = factory.getValidator();
}

请注意,这将是一种不同的类型,但它也包含具有相同签名的方法。validatorvalidate()

(选项 3)如果你确实关心 Spring 提供的额外设置,那么你可以编写一个 Spring 集成测试。我不建议使用,因为你会使你的测试膨胀。@SpringBootTest

您真正需要的只是包含必要的自动配置:SpringExtension

@ExtendWith(SpringExtension.class)
@ImportAutoConfiguration(ValidationAutoConfiguration.class)
public class MyValidationTest {
    @Autowired
    private LocalValidatorFactoryBean validator;
}