@MockMvc does not work with validation annotated with @Valid
Asked Answered
B

1

3

None of these solutions helped here: Spring mockMvc doesn't consider validation in my test. Added all of these dependencies, nothing helps.

I use Spring Boot 2.1.2, pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.2.RELEASE</version>
</parent>
<properties>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>

    <!-- Testing -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.github.springtestdbunit</groupId>
        <artifactId>spring-test-dbunit</artifactId>
        <version>1.3.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.dbunit</groupId>
        <artifactId>dbunit</artifactId>
        <version>2.5.4</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.jcabi</groupId>
        <artifactId>jcabi-matchers</artifactId>
        <version>1.3</version>
        <scope>test</scope>
    </dependency>

</dependencies>

I use standard hibernate validation '@NotNull':

@Entity
@Table(name="employee")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Employee  {

    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    protected Long id;

    @NotNull(message = "Please provide name")
    @Column(name = "name")
    private String name;

    @Column(name = "role")
    private String role;

    public Employee() {}

    public Employee(String name, String role) {
        this.name = name;
        this.role = role;
    }
}

@RestController
@RequestMapping(EmployeeController.PATH)
public class EmployeeController {

    public final static String PATH = "/employees";

    @Autowired
    private EmployeeService service;

    @PostMapping("")
    public Employee newEmployee(@RequestBody @Valid Employee newEmployee) {
        return service.save(newEmployee);
    }
}

@Service
public class EmployeeService {

    @Autowired
    private EmployeeRepository repository;

    public Employee save(Employee entity) {
        return getRepository().save(entity);
    }
}

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

}

Then I test my controller:

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
@DatabaseSetup("/employee.xml")
@TestExecutionListeners({
        TransactionalTestExecutionListener.class,
        DependencyInjectionTestExecutionListener.class,
        DbUnitTestExecutionListener.class
})
public class EmployeeControllerWebApplicationTest {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;
    private static String employeeRouteWithParam = EmployeeController.PATH + "/{id}";

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders
                .webAppContextSetup(context)
                .build();
    }

    @Test
    public void create_WithoutName_ShouldThrowException() throws Exception {
        String role = "admin";
        Employee expectedEmployee = new Employee(null, role);

        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        String json = ow.writeValueAsString(expectedEmployee);

        ResultActions resultActions = this.mockMvc.perform(post(PATH)
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .content(json))
                .andDo(print());

        String contentAsString = resultActions.andReturn().getResponse().getContentAsString();
        System.out.println("content: " + contentAsString); // empty body

        resultActions
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("error").exists())      // not exist!!!
                .andExpect(jsonPath("timestamp").exists()); // not exist!!!

    }
}

employee.xml:

<dataset>
    <Employee id="1" name="John" role="admin"/>
    <Employee id="2" name="Mike" role="user"/>
</dataset>

I can not understand why it does not work when I test through @MockMvc. What am i doing wrong? Status is correct, but there is no error content, empty response

But validation works if tested on a really running application, then everything works.

Beerbohm answered 10/3, 2019 at 14:16 Comment(8)
Can you update your question so that it is complete? The ID and EmployeeRepository classes are missing at the moment. The PATH constant is also missing in your tests as well as the employee.xml file.Guillen
If I try to fill in the pieces missing from your example, the call to the controller fails with a MethodArgumentNotValidException with the message Please provide name.Guillen
@AndyWilkinson you can see the whole project on my github github.com/Freeongoo/spring-examples/blob/master/…Beerbohm
@AndyWilkinson I added the requested modifications in the codeBeerbohm
@AndyWilkinson and please tell me, did you run a line in your example code: .andExpect(jsonPath("error").exists())?Beerbohm
The reponse is being validated and an exception is being thrown in your example. Your test is failing because you're really trying to test Spring Boot error infrastructure rather than your own controller. If you want to include the error infrastructure in your test, you should start a test with a complete web environment and use TestRestTemplate rather than MockMvc.Guillen
@AndyWilkinson but this is strange. Since interception of errors through @ControllerAdvice works correctly, why the same is not done for @ValidBeerbohm
Same problem for me : I use the latest SpringBoot 3.3.1 ==> the [at]Valid validation put on a JPA [at]Entity class do not trigger any hibernate exception / constraint violation when the REST controller is invoked by MockMvc or by WebTestClient *** BUT *** the hibernate exceptions / constraints violations are triggered when the REST controller is invoked by WebClientMews
M
0

I can reproduce this problem using Spring-boot 3.3.1
with MockMvc + @AutoConfigureMockMvc
or with WebTestClient + @AutoConfigureWebTestClient

It was solved by removing the annotation @Transactional from the JUNIT class test (and from all the test methods)

Therefore, the database constraints and the validation exceptions are thrown normally by the Hibernate's JPA layer.

Mews answered 26/7, 2024 at 13:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.