0

I would like to write some unit test to test the @Valid of SpringBoot request, hopefully, without having to spin the server.

For instance, in the "old way", one could write a piece of code like this:

record SomeRequest(String clientId) { }
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
class FieldValidationController {

    @PostMapping("/validate")
    String question(@RequestBody SomeRequest someRequest) {
        validateRequestOldFashionWay(someRequest);
        return "please validate the field";
    }

    //Not using @Valid here
    public void validateRequestOldFashionWay(SomeRequest someRequest) {
        if (someRequest.clientId() == null || someRequest.clientId().isEmpty()) {
            throw new IllegalArgumentException("clientId cannot be null or empty");
        }
    }

}
    @Test
    public void clientIdEmptyTestWithoutHavingToStartSpring() throws Exception {
        FieldValidationController controller = new FieldValidationController();
        SomeRequest someRequest1 = new SomeRequest("");
        assertThrows(IllegalArgumentException.class, () -> controller.validateRequestOldFashionWay(someRequest1));
        SomeRequest someRequest3 = new SomeRequest("good");
        controller.validateRequestOldFashionWay(someRequest3);
    }

As you can see above, it is a straightforward test on the validation method.

There is no need to spin up the server, bring in mockmvc, etc

Now, moving the code to Spring validation:

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;

record SomeRequest(@NotNull @NotEmpty String clientId) { }
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
class FieldValidationController {

    @PostMapping("/validate")
    String question(@Valid @RequestBody SomeRequest someRequest) {
        return "please validate the field";
    }

}

I understand I can write a unit test which looks like this to test the validation feature.

@WebMvcTest(FieldValidationController.class)
class FieldValidationControllerTest {

    @Autowired
    private MockMvc mvc;

        @Test
    public void clientIdNotNullTest() throws Exception {
        SomeRequest someRequest = new SomeRequest("notNullTest");
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, false);
        ObjectWriter ow = mapper.writer().withDefaultPrettyPrinter();
        String requestJson=ow.writeValueAsString(someRequest);
        mvc.perform(post("/validate").contentType(APPLICATION_JSON_UTF8)
                        .content(requestJson))
                .andExpect(status().isOk());
    }

But this would need to spin up spring, it looks more like an integration test now.

To keep the question simple, how can I test the same functionality, without having to spin up spring?

1
  • What do you want to test? That annotation is present in your method? then you can check it by reflection. Commented Jan 31 at 14:45

1 Answer 1

2

I think you are looking for this. First my DTO:

import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
public class TestDto {

    @NotBlank
    private String name;

}

and here is my test:

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import jakarta.validation.Validation;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Set;

class ValidatorUnitTest {

    @Test
    void test() {
        // prepare
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        // act
        Set<ConstraintViolation<Object>> validate1 = validator.validate(new TestDto("Patrick"));
        Set<ConstraintViolation<Object>> validate2 = validator.validate(new TestDto());

        // assert
        Assertions.assertEquals(0, validate1.size());
        Assertions.assertEquals(1, validate2.size());
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Hey @Patrick Rode, thank you for the answer. This is what I was looking for. Upvoted and accepted. Good day!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.