Я пытаюсь проверить, что мои bean-компоненты имеют правильные аннотации проверки. Я использую весеннюю загрузку. Вот пример тестового случая:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.validation.annotation.Validated;
@SpringBootTest
class ValidationTest {
@Test
void testConstructor() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> checkIfValidated(bean)).isInstanceOf(ConstraintViolationException.class);
}
@Test
void testSetter() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> bean.setSomeProperty(null)).isInstanceOf(ConstraintViolationException.class);
}
private void checkIfValidated(@Valid TestedBean bean) {
}
@Validated
class TestedBean {
@NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(@NotNull String someProperty) {
this.someProperty = someProperty;
}
}
}
Я ожидаю, что вызов checkIfvalidated()
и setSomeProperty(null)
поднимет ConstraintViolationException
, и тесты пройдут, но они оба терпят неудачу с:
java.lang.AssertionError:
Expecting code to raise a throwable.
at com.example.sandbox.ValidationTest.test(ValidationTest.java:20)
...
Мой пом.xml:
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>com.example.springbootsandbox</artifactId>
<version>0.0</version>
<name>SpringBootSandbox</name>
<description>Sandbox for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.5.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Почему здесь не поднят ConstraintViolationException
? Свойство bean-компонента имеет аннотацию @NotNull
, сам bean-компонент является @Validated
, а сигнатура метода требует bean-компонента @Valid
.
Есть ли простой способ вызвать это исключение в контексте моего тестового класса?
Когда я использую аннотации проверки сигнатур методов для интерфейса службы, все работает так, как ожидалось. Я не понимаю, где разница.
Интерфейс сервиса:
package com.example.sandbox;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
@Validated
public interface IService {
public void setValue(@NotNull String value);
}
Реализация услуги:
package com.example.sandbox;
import org.springframework.stereotype.Service;
@Service
public class SomeService implements IService {
@Override
public void setValue(String value) {
// Do nothing
}
}
Прецедент:
package com.example.sandbox;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import javax.validation.ConstraintViolationException;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SomeServiceTests {
@Autowired
IService service;
@Test
void testSetValue() {
assertThatThrownBy(() -> service.setValue(null)).isInstanceOf(ConstraintViolationException.class);
}
}
==> Тест пройден.
Рабочий код в соответствии с данным ответом:
Тестовый класс:
@SpringBootTest
class ValidationTest {
@Autowired
private Validator validator; // Using the default validator to test property annotations
@Autowired
private TestedBeanService service; // Using a service to test method annotations
@Test
void testPropertyAnnotations() {
TestedBean bean = new TestedBean(null);
Set<ConstraintViolation<TestedBean>> violations = validator.validate(bean);
assertThat(violations).isNotEmpty();
}
@Test
void testMethodAnnotations() {
TestedBean bean = new TestedBean(null);
assertThatThrownBy(() -> service.setBeanProperty(bean, null)).isInstanceOf(ConstraintViolationException.class);
}
}
Тестируемый боб:
@Validated
class TestedBean {
@NotNull
private String someProperty;
public TestedBean(String someProperty) {
super();
this.someProperty = someProperty;
}
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) { // No more annotation on setter
this.someProperty = someProperty;
}
}
Интерфейс сервиса:
@Validated
public interface TestedBeanService {
// method annotation on the interface method
void setBeanProperty(TestedBean bean, @NotNull String someProperty);
}
Реализация услуги:
@Service
public class TestedBeanServiceImpl implements TestedBeanService {
@Override
public void setBeanProperty(TestedBean bean, String someProperty) {
bean.setSomeProperty(someProperty);
}
}
Почему здесь не поднят
ConstraintViolationException
? Свойство bean-компонента имеет аннотацию@NotNull
, сам bean-компонент является@Validated
, а сигнатура метода требует bean-компонента@Valid
.
Аннотации сами по себе ничего не значат, их надо как-то обрабатывать. В этом случае аннотация @Validated
обрабатывается Spring для своих компонентов. Тест не является bean-компонентом Spring, поэтому инфраструктура не рассматривает аннотации, связанные с валидацией, поэтому не является исключением.
Даже если тест был Spring Bean, подход может не работать из коробки. Подробнее см. этот вопрос.
Есть ли простой способ вызвать это исключение в контексте моего тестового класса?
Взгляните на этот вопрос
Когда я использую аннотации проверки сигнатур методов для интерфейса службы, все работает так, как ожидалось. Я не понимаю, где разница.
Это происходит потому, что service
является компонентом Spring, а test — нет. Когда вызывается метод в service
, он перехватывается MethodValidationInterceptor, что не относится к тесту.
Хорошо. Таким образом, мои объекты проксируются, а прокси занимаются проверкой. Я обнаружил некоторые ограничения, но я думаю, что они будут направлять мой код: - Я использую аннотации проверки для свойств своих компонентов и могу проверить их с помощью
Validator
- Мне не удается активировать аннотации проверки для сеттеров моих компонентов - Я могу поставитьConstraintValidationException
своим методам обслуживания, если я использую аннотации проверки в его методе интерфейса. При непосредственном использовании объекта это не работает. Итак, я буду использовать аннотации свойств для своих bean-компонентов и аннотации методов для своих сервисов. спасибо