java更改JUnit测试顺序会根据Hibernate Validator的平台ResourceBundleLocator使用来自多个JAR文件的验证消息中断测试
我有一个GradleSpring Boot应用程序,使用Hibernate Validator在Java11上运行。该应用程序使用多个带有custom validation constraint annotations的自定义库jar,每个jar都有自己的ValidationMessages.properties
文件,其中包含这些注释的默认消息。使用Hibernate的PlatformResourceBundleLocator中的内置功能,可以将多个JAR文件中的ValidationMessages.properties
文件聚合到一个包中,从而支持这一点:
@Configuration
public class ValidationConfig {
@Bean
public LocalValidatorFactoryBean validator() {
PlatformResourceBundleLocator resourceBundleLocator =
new PlatformResourceBundleLocator(ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, null, true);
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setMessageInterpolator(new ResourceBundleMessageInterpolator(resourceBundleLocator));
return factoryBean;
}
}
jar1:验证消息。属性
com.example.CustomValidation1.message=My custom message 1
jar2:验证消息。属性
com.example.CustomValidation2.message=My custom message 2
有相当多的单位和;测试验证功能的项目中的集成测试。其中一些测试在Spring聚合消息验证程序bean中自动连接。一些测试(尤其是在应用程序使用多个ValidationMessages.properties
之前的测试)不依赖于返回的确切消息,并且使用默认的Spring验证器,而不使用消息聚合。虽然更新旧的测试可能是有意义的,但出于优先级和可用时间的考虑,该任务已推迟到未来
运行应用程序时,消息聚合功能正在按预期工作。当我在本地机器上运行测试时,它也按预期工作。然而,当我的项目的测试通过Jenkins持续集成工具在构建服务器上运行时,一些验证测试失败
我已经根据JUnit测试类的运行顺序确定了失败的发生。测试在本地的运行顺序与在Jenkins上不同(这是允许的,因为Gradle JUnit插件不保证测试类的执行顺序)。具体来说,任何测试是否失败取决于使用消息聚合验证器的测试是否在使用没有消息聚合的验证器的测试之前运行
我已经能够将这个问题归结为一个简单的可在单个测试类中重新创建的失败,如下所示。为了示例的简单性,使用@CustomValidation1
&@CustomValidation2
已编写验证注释,以确保验证始终失败
import com.example.CustomValidation1;
import com.example.CustomValidation2;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
import org.hibernate.validator.resourceloading.PlatformResourceBundleLocator;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.Set;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ValidationMessageTests {
private Validator aggregateMessageValidator = createAggregateMessageValidator();
private Validator standardValidator = createBasicValidator();
private Validator createBasicValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.afterPropertiesSet();
return factoryBean;
}
private Validator createAggregateMessageValidator() {
PlatformResourceBundleLocator resourceBundleLocator =
new PlatformResourceBundleLocator(ResourceBundleMessageInterpolator.USER_VALIDATION_MESSAGES, null, true);
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
factoryBean.setMessageInterpolator(new ResourceBundleMessageInterpolator(resourceBundleLocator));
factoryBean.afterPropertiesSet();
return factoryBean;
}
@Test
public void test1() {
Set<ConstraintViolation<MyInput>> violations = aggregateMessageValidator.validate(new MyInput());
assertEquals(Set.of("My custom message 1", "My custom message 2"),
violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.toSet()));
}
@Test
public void test2() {
Set<ConstraintViolation<MyInput>> violations = standardValidator.validate(new MyInput());
assertEquals(2, violations.size());
}
@CustomValidation1
@CustomValidation2
private static class MyInput {
}
}
当test1
在test2
之前运行时,两个测试都通过。但是,当test2
被重命名为test0
以便在test1
之前运行时,test0
通过,但test1
失败,出现以下错误:
java.lang.AssertionError:
Expected :[My custom message 1, My custom message 2]
Actual :[My custom message 1, {com.example.CustomValidation2.message}]
为什么更改测试顺序会导致这些测试失败,我该如何修复它
该代码目前正在使用Spring Boot 2.2.4。使用Hibernate Validator 6.0.18发布。决赛
# 1 楼答案
这个问题的根本原因是默认情况下会缓存
ResourceBundle
。根据ResourceBundle JavaDocs:由于这种缓存,无论哪个测试首先导致Hibernate Validator加载} ,而不是所需的^{} 。当
ValidationMessages.properties
文件,都将确定哪个ResourceBundle
用于所有后续测试。首先运行未使用自定义验证配置的测试时,将为ValidationMessages
缓存非聚合的^{PlatformResourceBundleLocator
加载资源束时,聚合逻辑将被忽略,因为已缓存的ResourceBundle
将被使用解决办法很简单} 方法来清除缓存,可以在Hibernate Validator检索包以创建验证消息之前调用该方法:
ResourceBundle
有一个^{