当前位置: 首页 > 知识库问答 >
问题:

Spring Boot UnitTest:@ConstraintValidator中的值

韦嘉颖
2023-03-14

我目前正在提供覆盖——通过MockMVC请求调用测试我的DTO的验证。最近,我在我的应用程序的SupportAlitVisulations字段中引入了新的值。易于维护和扩展的特性。请参阅下面的代码片段:

@Component
public class RegistrationValidator implements ConstraintValidator<Registration, String> {

    //campus.students.supportedspecializations="J2E,.NET,OracleDB,MySQL,Angular"

    @Value("${campus.students.supportedspecializations}")
    private String supportedSpecializations;

    private String specializationExceptionMessage;

    //All ExceptionMessages are maintained in a separate class
    @Override
    public void initialize(Registration constraintAnnotation) {
        exceptionMessage = constraintAnnotation.regionException().getMessage();
    }

    @Override
    public boolean isValid(RegistrationData regData, ConstraintValidatorContext context) {

        String[] specializations = supportedSpecializations.split(",");
        boolean isValidSpecialization = Arrays.stream(specializations)
                    .anyMatch(spec -> spec.equalsIgnoreCase(regData.getSpec()));
        if (!isValidSpecialization){
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(specializationExceptionMessage)
                        .addConstraintViolation();
            return false;
            }
        //additional validation logic...
        return true;
    }
}

单元测试现在失败,因为该字段没有被@Value注释的定义属性注入。我不确定ReflexiTestUtils是否可以帮助我,因此非常感谢关于如何在UnitTest中注入所需值的任何建议。

Spring版本是2.1.0,我目前正在使用以下代码段进行测试:

@InjectMocks
private StudentController mockRestController;

@Mock
private StudentService mockStudentService;

@Mock
private ValidationExceptionTranslator mockExceptionTranslator;

@Value("${campus.students.supportedspecializations}")
private String supportedSpecializations;

private MockMvc mockMvc;

private static final String VALIDATION_SUCCESSFUL = "success";
private static final String VALIDATION_FAILED = "failed";

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    this.mockMvc = MockMvcBuilders.standaloneSetup(mockRestController).build();

    doReturn(
            ResponseEntity.status(HttpStatus.OK)
            .header("Content-Type", "text/html; charset=utf-8")
            .body(VALIDATION_SUCCESSFUL))
    .when(mockStudentService).insertStudent(Mockito.any());

    doReturn(
            ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .header("Content-Type", "application/json")
            .body(VALIDATION_FAILED))
    .when(mockExceptionTranslator).translate(Mockito.any());
}

@Test
public void testValidation_UnsupportedSpecialization() throws Exception {

    MvcResult mvcResult = mockMvc.perform(
            post("/Students").contentType(MediaType.APPLICATION_JSON_UTF8).content(
                    "{\"registrationData\":{\"spec\":\"unsupported\"}}"))
            .andExpect(status().isBadRequest())
            .andReturn();

    assertEquals(VALIDATION_FAILED, mvcResult.getResponse().getContentAsString());

    verify(mockExceptionTranslator, times(1)).translate(Mockito.any());
    verify(mockStudentService, times(0)).insertStudent(Mockito.any());
}

我试着用@RunWith(SpringRunner.class)和@SpringBootTest(classes=Application.class)注释我的测试类,但由于@Value未被解析,验证测试仍然失败。我可能错了,但我认为ConstraintValidator的实例是在我们到达restController之前创建的,所以MockMVC执行(…)调用不能简单地确保验证器中适当的@Value被注入到supportedSpecializations中。

共有3个答案

秦经义
2023-03-14

我认为最好的选择是在注册验证器中使用构造函数注入。类,以便您可以在需要时直接为测试分配模拟值或测试值。例子:

@Component
class ExampleClass {

    final String text

    // Use @Autowired to get @Value to work.
    @Autowired
    ExampleClass(
        // Refer to configuration property
        // app.message.text to set value for 
        // constructor argument message.
        @Value('${app.message.text}') final String text) {
        this.text = text
    }

}

通过这种方式,您可以将模拟值设置为用于单元测试的变量。是的,您是对的,这里不可以选择自定义构造函数,然后您可以引入一个配置类,其中您可以从yml或属性中读取这些值,并在验证器中自动绑定这些值。这应该适合您。

您可以在单独的测试中提供@Value属性。yml测试。属性,并指定在运行集成测试时要使用的属性。在这种情况下,您应该能够解析这些值。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ExampleApplication.class)
@TestPropertySource(locations="classpath:test.properties")
public class ExampleApplicationTests {

}

@TestProperty tySource注释具有更高的优先级顺序,它应该解析您的值。

汲昊空
2023-03-14

是的,使用ReflexiTestUtil

使用ReflectionTestUtil。setField设置setup()方法(junit)中supportedSpecializations的值

更多细节
我建议不要在单元测试中使用MockMVC;它适用于集成测试,而不是单元测试。

单元测试不需要启动Spring;您永远不需要Spring来为单元测试执行注入。相反,实例化正在测试的类并直接调用这些方法。

下面是一个简单的例子:

public class TestRegistrationValidator
{
  private static final String VALUE_EXCEPTION_MESSAGE = "VALUE_EXCEPTION_MESSAGE";
    private static final String VALUE_SUPPORTED_SPECIALIZATIONS = "BLAMMY,KAPOW";

    private RegistrationValidator classToTest;

    @Mock
    private Registration mockRegistration;

    @Mock
    private RegionExceptionType mockRegionExceptionType; // use the actual type of regionExcpeption.

    @Before
    public void preTestSetup()
    {
        MockitoAnnotations.initMocks(this);

        ReflectionTestUtils.setField(classToTest, "supportedSpecializations", VALUE_SUPPORTED_SPECIALIZATIONS);

        doReturn(VALUE_EXCEPTION_MESSAGE).when(mockRegionExceptionType).getMessage();

        doReturn(mockRegionExceptionType).when(mockRegion).regionException();
    }

    @Test
    public void initialize_allGood_success()
    {
        classToTest.initialize(mockRegistration);

        ...assert some stuff.
        ...perhaps verify some stuff.
    }
}
曹均
2023-03-14

通过以下方式解决了这个问题:在测试类中添加了以下注释

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc

然后自动装配控制器和mockMVC,最后使用Spring的@MockBean注释服务和翻译器

所以现在看起来是这样的:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
public class StudentValidatorTest {

    @Autowired
    private StudentController mockRestController;

    @MockBean
    private StudentService mockStudentService;

    @MockBean
    private ValidationExceptionTranslator mockExceptionTranslator;

    @Autowired
    private MockMvc mockMvc;

    private static final String VALIDATION_SUCCESSFUL = "success";
    private static final String VALIDATION_FAILED = "failed";

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(mockRestController).build();

        doReturn(
            ResponseEntity.status(HttpStatus.OK)
            .header("Content-Type", "text/html; charset=utf-8")
            .body(VALIDATION_SUCCESSFUL))
        .when(mockStudentService).insertStudent(Mockito.any());

        doReturn(
                ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .header("Content-Type", "application/json")
                .body(VALIDATION_FAILED))
        .when(mockExceptionTranslator).translate(Mockito.any());
    }

//...and tests...
 类似资料:
  • 我已经混合了斯卡拉-Java spring-引导mvc项目。我试图使用 实现请求bean的自定义验证器,我从这里和其他线程中了解到,用Java编写注释更好,scala支持JSR-303验证吗? 我用Java编写了自定义约束注释,用scala编写了ConstraintValidator类,但当我指定@Constraint(validatedBy=ScalacustomerValidator.clas

  • 我想在上传使用ConstraintValidator时检查文件扩展名。如果文件扩展名没有在注释中指定,那么用户应该会得到常见的约束验证错误响应。但是当它发生时,会发生此异常 和用户得到坏请求页面 我的类:注解: 类上传文件类型验证器: 和控制器(永不到达,因为isValid总是返回false: 为什么会发生这种异常,以及如何避免?

  • 我有一个这样的处理程序和一个自定义注释@validrequest: 注释本身看起来是这样的: 而验证器是这样的: 问题是验证被完全忽略了。我可以发送任何事件有或没有身体和一切工作无一例外。我做的一切都是根据Micronout文档,会有什么问题吗? https://docs.micronaut.io/latest/guide/index.html#BeanValidation

  • 我有一个“AllowedValuesValidator.java”类: 以及相应的接口类: 我希望能够编写一个单元测试类来测试验证器中的直接逻辑。但我在谷歌上搜索的大多数地方似乎都给出了测试类的例子,我们基本上测试了给定模型类的所有验证器,例如: 我不想构建模拟模型来测试所有验证器。有没有一种方法可以创建一个单独的测试类,只在一个验证器中直接测试逻辑,而不使用任何其他模型类等?

  • 问题内容: 我有一个简单的验证器来验证String值是否是预定义列表的一部分: 例如,它将验证: 我想为字符串列表创建一个验证器以验证如下内容: 我尝试了这个: 问题是,如果list包含2个或多个非法值,则将只有一个(第一个)约束违规。我希望它有多个。我应该怎么做? 问题答案: 您当前的代码有2个问题: 在您的方法中,您应该像这样遍历给定列表的所有元素(设置适当的标志): 第二个是针对约束违反的实

  • 下午好 我在Bean Validator(JSR 303)中注入服务时遇到问题。我将执行验证,但我需要在数据库中验证此记录; 当我使用我的服务时,它会抛出NullPointerException; 例外: 注释: ValidatorImpl: