我目前正在提供服务-通过MockMVC请求调用测试我的DTO的验证。 最近,我在我的Registration ConstraintValidator中引入了一个新字段,supportedSpecializations,我从application.properties中注入值,以便于维护和扩展。参见下面的代码片段:
@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批注的定义属性未注入该字段。 我不确定 ReflectionTestUtils 是否可以解决我的问题,因此,有关如何在UnitTests中注入所需值的任何建议都将不胜感激。
春季版本为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 perform(...)调用不能仅仅确保在验证器中获取适当的@Value注入支持的Specializations。
答案 0 :(得分:0)
是的,
使用ReflectionTestUtil
。
使用ReflectionTestUtil.setField
来设置supportedSpecializations
的值。
setup()
方法(junit <1.4)
或在单元测试中使用@Before
带注释的方法(junit> 1.4)。
更多详细信息
建议不要在单元测试中使用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.
}
}
答案 1 :(得分:0)
我认为,最好的选择是在RegistrationValidator.class
中使用构造函数注入,以便在需要时也可以直接为测试分配模拟或测试值。示例:
@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或property中读取这些值并在验证器中自动装配这些值。
或
您可以在单独的@Value
或test.yml
中提供test.properties
属性,并指定在运行集成测试时要使用的属性。在这种情况下,您应该能够解析这些值。
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = ExampleApplication.class)
@TestPropertySource(locations="classpath:test.properties")
public class ExampleApplicationTests {
}
@TestPropertySource
注释的优先级较高,应该可以解析您的值。
答案 2 :(得分:0)
通过以下方式解决了该问题: 在测试类中添加了以下注释
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
@AutoConfigureMockMvc
然后自动连接 controller 和 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...