how to do unit test validation annotations in spring
Asked Answered
R

3

10

I have some annotation in a class such as

 public class ProductModel {
@Pattern(regexp="^(1|[1-9][0-9]*)$", message ="Quantity it should be number and greater than zero")
private String  quantity;

then in my controller

@Controller
public class Product Controller
private ProductService productService;
@PostMapping("/admin/product")
public String createProduct(@Valid @ModelAttribute("product") ProductModel productModel, BindingResult result)
{
    // add println for see the errors
    System.out.println("binding result: " + result);

    if (!result.hasErrors()) {
        productService.createProduct(productModel);
        return "redirect:/admin/products";
    } else {
        return "product";
    }
}

Then I am trying to do a test of createProduct from ProductController.

@RunWith(MockitoJUnitRunner.class)
public class ProductControllerTest {

@Autowired
private MockMvc mockMvc;

@Mock
ProductService productService;

@InjectMocks
ProductController productController;

@Mock
private BindingResult mockBindingResult;

@Before
public void setupTest() {
    MockitoAnnotations.initMocks(this);
    Mockito.when(mockBindingResult.hasErrors()).thenReturn(false);
}


@Test
public void  createProduct() throws Exception {

    productController = new ProductController(productService);      
   productController.createProduct(new ProductModel(), mockBindingResult);

Here I do not know how can I add values to the object productmodel and also how can I test the message output of "...number should be greater than zero". What I was trying to do it was create an object and then assert with values for making it fail or work such as assertEquals(hello,objectCreated.getName()); Any advice or help will be highly appreciated.

Runin answered 15/3, 2019 at 15:38 Comment(0)
D
27

To validate bean annotations you must have the context in execution. You can do this with:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)

Then your tests will validate the annotations.

However, if you just want to validate the annotation of model (without another business rules) you can use a validator:

private static ValidatorFactory validatorFactory;
private static Validator validator;

@BeforeClass
public static void createValidator() {
    validatorFactory = Validation.buildDefaultValidatorFactory();
    validator = validatorFactory.getValidator();
}

@AfterClass
public static void close() {
    validatorFactory.close();
}

@Test
public void shouldReturnViolation() {
    ProductModel productModel = new ProductModel();
    productModel.setQuantity("a crazy String");

    Set<ConstraintViolation<ProductModel>> violations = validator.validate(productModel);

    assertFalse(violations.isEmpty());
}
Damselfish answered 16/3, 2019 at 0:12 Comment(3)
Hi Allan, yes I realize I have to do an IT to validate if my annotations were working or not. Thanks for your help!Runin
This is JUNIT4 way of doing, please do not go for this way in 2024Coactive
@Sören @RunWith is an old JUnit4 annotation. To avoid deprecation and if you already have JUnit5 in your project, it would be better to use ExtendWith(SpringExtension.class) Same goes for BeforeClass and AfterClass. You can replace them with BeforeAll and AfterAllCoactive
B
1

Adding to Allan Moreira Leite's answer you can make the test setup more concise by using a single beforeAll() method:

private static Validator validator;

@BeforeAll
static void beforeAll() {
    try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
        validator = factory.getValidator();
    }
}
Borreri answered 12/12, 2023 at 12:18 Comment(0)
A
-1

Just use setter of your Model

ProductModel productModel = new ProductModel();
productModel.setQuantity("a crazy String");
productModel.setAnotherValueOfThatModel(true);
productController.createProduct(new ProductModel(), mockBindingResult);
Alvera answered 15/3, 2019 at 16:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.