I'm working with Quarkus and facing weird scenario where #Valid annotation not working as expected in JUnit but working fine in REST API.
Any help would be appreciated.
Thanks in advance.
Test
#Test
void
validateContent_GivenInvalidFieldName_ThrowConstraintViolationException() {
// Arrange
String invalidFieldName =
"{\"i\": \"Test\"}";
// Act, Assert
assertThrows(
ConstraintViolationException.class,
() -> service.validate(invalidFieldName),
"ConstraintViolationException exception should be thrown");
}
Service class method
public void validate(String jsonString){
try {
ValidField validField = convert(jsonString);
validateContent(validField);
} catch (JsonProcessingException e) {
createValidationError(e.getMessage(), "content");
}
}
void validateContent(
#Valid ValidField content) {
// #Valid doing the work here
}
Model class
public class ValidField{
private String id;
#NotBlank
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
The #Valid annotation does not do anything by itself, it's just a "metadata" instructing an validator implementation to validate the entity. In your rest endpoint is working because it's a bean managed by quarkus, and the framework will automatically weave the validator for you. This will not happen in a plain junit test. The easiest way to make it work in your case, is to turn the validator into a bean and create a test using the QuarkusTest, like this:
#ApplicationScoped
public class YourValidatorService() {
public void validate(String jsonString) {
try {
ValidField validField = convert(jsonString);
validateContent(validField);
} catch (JsonProcessingException e) {
createValidationError(e.getMessage(), "content");
}
}
void validateContent(
#Valid ValidField content) {
// #Valid doing the work here
}
}
and injecting into your test using the #QuarkusTest annotation
#QUarkusTest
public class YourValidatorServiceTest() {
#Inject
private YourValidatorService service;
#Test
void validateContent_GivenInvalidFieldName_ThrowConstraintViolationException() {
// Arrange
String invalidFieldName =
"{\"i\": \"Test\"}";
// Act, Assert
assertThrows(
ConstraintViolationException.class,
() - > service.validate(invalidFieldName),
"ConstraintViolationException exception should be thrown");
}
}
By doing that, you will enable the Quarkus CDI on tests, which will weave the validator for you.
You can read a little bit more about how the validations work, here.
Related
Hi I have this simple code for my Spring Boot Project:
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(id))
.userName(rs.getString(userName)).build();
}
}
what I want is to create a simple Mockito Test that will check #Value strings like so:
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
Environment environment;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(environment.getProperty("user.id")).thenReturn("id");
when(environment.getProperty("user.userName")).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
But I can't find a simple way to check if the value I injected is what I expect.
any ideas?
Unfortunately, there is no mocking mechanism for Spring's #Value. However, you can use a simple workaround using ReflectionUtils that serves for this purpose according to the JavaDoc:
ReflectionTestUtils is a collection of reflection-based utility methods for use in unit and integration testing scenarios.
There are often times when it would be beneficial to be able to set a non-public field, invoke a non-public setter method, or invoke a non-public configuration or lifecycle callback method when testing code involving
ReflectionTestUtils.setField(userRowMapper, "id", "my-id-value");
ReflectionTestUtils.setField(userRowMapper, "userName", "my-userName-value");
JavaDoc for ReflectionTestUtils#setField(Object, String, Object).
Add getter methods for id and userName fields instead of mocking Environment class.
#Component
public class UserRowMapper implements RowMapper<User> {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(getId()))
.userName(rs.getString(getUserName())).build();
}
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
While mocking:
Mockito.when(userRowMapper.getId()).thenReturn("id");
Mockito.when(userRowMapper.getUserName()).thenReturn("userName");
Also, you can use TestPropertySource annotation to provide altogether different properties file:
#SpringBootTest
#TestPropertySource(locations = "/application2.properties")
public class TestClassTest {
#Autowired
TestClass testClass;
#Test
public void test() {
assertEquals("id", testClass.getId());
}
}
I would rather suggest to you to do not use inline #Value annotation on the consumer class. As you have seen, the class testability decreases.
You can solve your problem simply creating a #Configuration bean and injecting it to the UserRowMapper class. In this way, using DI you can easily mock the configuration in your tests.
See below a naïve implementation.
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
#Component
public class UserRowMapper implements RowMapper<User> {
private UserRowMapperConfiguration configuration;
public UserRowMapper (UserRowMapperConfiguration configuration) {
this.configuration = configuration;
}
#Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
return User.builder()
.id(rs.getInt(this.configuration.getId()))
.userName(rs.getString(this.configuration.getUserName())).build();
}
}
#ExtendWith(MockitoExtension.class)
class UserRowMapperTest {
#Mock
UserRowMapperConfiguration configuration;
#Mock
ResultSet resultSet;
#InjectMocks
UserRowMapper userRowMapper;
#Test
void testMapRow() {
when(configuration.getId()).thenReturn("id");
when(configuration.getUserName()).thenReturn("userName");
try {
final User user = userRowMapper.mapRow(resultSet, anyInt());
// check if its ok
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
As thepaoloboi suggested use a configuration class to hold all your configs.
Now to test that your config is pointing to the right #Value key, you create an integration test by simply loading that object using spring without loading the whole context. That way it'll be as fast as a unit test.
Here's an example:
#ExtendWith(SpringExtension.class)
#Import(UserRowMapperConfiguration.class)
#TestPropertySource(properties = { "user.id=id" , "user.userName=userName"})
class UserRowMapperConfigurationTest {
#Autowired
UserRowMapperConfiguration userRowMapperConfiguration;
#Test
void test() {
assertEquals("id",userRowMapperConfiguration.getId());
assertEquals("userName",userRowMapperConfiguration.getUserName());
}
}
and Configuration class:
#Configuration
public class UserRowMapperConfiguration {
#Value("${bug.value}")
private String id;
#Value("${wrong.value}")
private String userName;
public String getId() {
return id;
}
public String getUserName() {
return userName;
}
}
I am having problems testing my HandleException, even though I have searched I only find solutions for a standard controller that integrates error management but I have it separately.
The problem is that when it comes to doing the unit tests and its coverage, I don't know how to cover it properly.
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(AmazonClientException.class)
protected ResponseEntity<Object> handleAmazonClientException(AmazonClientException ex) {
return buildResponseEntity(new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex));
}
#ExceptionHandler(AmazonS3Exception.class)
protected ResponseEntity<Object> handleAmazonS3Exception(AmazonS3Exception ex) {
return buildResponseEntity(new ApiError(HttpStatus.NOT_FOUND, ex));
}
private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
}
RestExceptionHandler.class
ApiError.class
#Data
public class ApiError {
private HttpStatus status;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
private LocalDateTime timestamp;
private String message;
private String debugMessage;
private ApiError() {
setTimestamp(LocalDateTime.now());
}
ApiError(HttpStatus status) {
this();
setStatus(status);
}
ApiError(HttpStatus status, Throwable ex) {
this();
setStatus(status);
setMessage("Unexpected error");
setDebugMessage(ex.getLocalizedMessage());
}
ApiError(HttpStatus status, String message, Throwable ex) {
this();
setStatus(status);
setMessage(message);
setDebugMessage(ex.getLocalizedMessage());
}
}
How can see, i only use my RestExceptionHandler for that, not call another Business class.
Any advice appreciated.
To unit test #ControllerAdvice annotated classes, you can use something called MockMvc which Spring provides.
It is a very elegant way of unit testing your controllers and subsequent controller advices.
So, let's say this is my controller
#RestController
#RequestMapping("api")
public class RobotController {
private RobotService robotService;
#GetMapping
public Collection<Integer> get() {
throw new DemoException();
}
}
And here's my controller advice; DemoException is just an empty class that extends RuntimeException.
#RestControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(DemoException.class)
public ResponseEntity<Object> handleException(DemoException dex) {
return ResponseEntity
.status(400)
.body("Bad request");
}
}
So, this means, sending a GET request to BASEURL/api will give me Bad request text response with 400 status code.
Okay, since we are done with the setup, let's move to the actual unit test. This is a very simple unit test, but it does what you need.
public class AdviceTest {
private final MockMvc mockMvc = MockMvcBuilders
.standaloneSetup(new RobotController())
.setControllerAdvice(new RestExceptionHandler())
.build();
#Test
void testGetFails() {
mockMvc.perform(
MockMvcRequestBuilders.get("/api")
).andExpect(
status().isBadRequest()
);
}
}
So, let me explain the initialization of the MockMvc object. There are two versions.
Standalone setup in which you just provide manually created controller objects.
Web application context in which you just provide an autowired application context.
You can also autowire MockMvc object.
Out of those 2, I really like the former, because it does not include the whole Spring context, hence it is faster.
There are a lot of methods available from checking the json content to paths. You can read more here:
Spring Guide
Baeldung
I have a class:
#Component
public class ContractorFormValidator implements Validator {
Logger logger = LoggerFactory.getLogger(ContractorFormValidator.class);
#Inject IBusinessDataValidator businessDataValidator;
#Override
public boolean supports(Class<?> clazz) {
return Contractor.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Contractor contractor = (Contractor) target;
if (!businessDataValidator.isNipValid(contractor.getContractorData().getNip())) {
errors.rejectValue("contractorData.nip", "invalid");
}
if (!businessDataValidator.isRegonValid(contractor.getContractorData().getRegon())) {
errors.rejectValue("contractorData.regon", "invalid");
}
}
}
How can I test it? I have tried this: How to test validation annotations of a class using JUnit? but this doesn't work cause the validate method in my validator requires Errors class passed to it's method signature.
I have no Idea if I can pass this Errors object to the validator. Is there any other way?
Have you tried to write a simple unit test for this?
#RunWith(SpringJUnit4ClassRunner.class)
public class ContractorFormValidatorTest {
#Autowired
private ContractorFormValidator validator;
#Test
public void testValidation() throws Exception {
Contractor contractor = new Contractor();
// Initialise the variables here.
Errors errors = new BeanPropertyBindingResult(contractor, "contractor");
validator.validate(contract, errors);
// If errors are expected.
Assert.assertTrue(errors.hasErrors());
for (Error fieldError : errors.getFieldErrors()) {
Assert.assertEquals("contractorData.nip", fieldError.getCode());
}
}
}
If you are going to use the validator in a controller implementation, then you need to use the MockMvc apis
Your set up can be included in the class above.
private MockMvc mockMvc
#Autowired
private MyController controller;
#Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.standaloneSetup(this.controller).build();
}
#Test
public void testMethod() {
MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.post("/yoururl")).
andExpect(MockMvcResultMatchers.status().isCreated()).andReturn();
}
Use the class org.springframework.validation.BeanPropertyBindingResult,
Errors newErrors = new BeanPropertyBindingResult(validateObject, "objectName");
Bit of a repost, but a certain catch-22 about not having enough reputation means I can't comment on any of the duplicate threads! (cough cough)
I'm trying to test the catch block of a try-catch using Mockito; is it possible to make a mock throw an exception that is handled by the method being tested? I can't use doThrow()...when()... or #Test(expected = Foo.class) because the exception is handled. I want to test that the method handles Exceptions properly.
#Controller
public class StockExchangeController {
public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {
try {
validator.validate(myObj); // Throws CustomException if validation fails
mav.setViewName("successPage");
} catch (CustomException ex) {
mav.setViewName("failPage");
}
return mav;
}
}
I'd like to be able to stub the behaviour of my "validatorObject", like
doThrow(new CustomException()).when(validatorMock).validate();
Is there a way to do this?
The answer here (Test catch block logic with Junit and mockito) doesn't work (I believe) because the exception is handled before it gets to test level.
Tips and thoughts much appreciated!
BDD Style Solution
Mockito alone is not the best solution for handling exceptions, use Mockito with Catch-Exception
Mockito + Catch-Exception + AssertJ
given(otherServiceMock.bar()).willThrow(new MyException());
when(myService).foo();
then(caughtException()).isInstanceOf(MyException.class);
Sample code
Mockito + Catch-Exception + Assertj full sample
Dependencies
eu.codearte.catch-exception:catch-exception:1.3.3
org.assertj:assertj-core:1.7.0
Disadvantage
Only Mockito up to 1.10.8 is supported
Why should doThrow(..).when(..)... not work?
The placeOrder method catches the exception and returns a testable result:
#RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {
#Mock
Validator validator;
#Mock MyObj myObj;
#Test
public void testException() throws CustomException {
StockExchangeController sec = new StockExchangeController(validator);
doThrow(new CustomException()).when(validator).validate(myObj);
ModelAndView modelAndView = sec.placeOrder(new ModelAndView(), myObj);
assertEquals("failPage", modelAndView.getViewName());
}
}
I've tested with these two files:
Main source code:
//src/main/java/StockExchangeController.java
public class StockExchangeController {
private ValidationFactory factory;
public ModelAndView placeOrder(ModelAndView mav, MyObj myObj) {
Validator validator = factory.getValidator("S");
try {
validator.validate(myObj); // Throws CustomException if validation fails
mav.setViewName("successPage");
} catch (CustomException ex) {
mav.setViewName("failPage");
}
return mav;
}
}
class CustomException extends Exception {}
interface ValidationFactory {
Validator getValidator(String s);
}
class Validator {
public void validate(MyObj myObj) throws CustomException {}
}
class ModelAndView {
private String viewName;
public void setViewName(String viewName) {
this.viewName = viewName;
}
public String getViewName() {
return viewName;
}
}
class MyObj {}
Test source code:
//src/test/java/TestStockExchangeController.java
//various imports
#RunWith(MockitoJUnitRunner.class)
public class TestStockExchangeController {
#Mock
private Validator validator;
#Mock
private ValidationFactory validationFactory;
#InjectMocks
StockExchangeController target = new StockExchangeController();
#Test
public void testException() throws CustomException {
MyObj myObj = new MyObj();
when(validationFactory.getValidator(anyString())).thenReturn(validator);
doThrow(new CustomException()).when(validator).validate(myObj);
ModelAndView modelAndView = target.placeOrder(new ModelAndView(), myObj);
assertEquals("failPage", modelAndView.getViewName());
verify(validator).validate(myObj);
}
}
I am writing unit test for my Jersey rest API which uses MyBatis at the background.
This is the structure of my classes:
rest service:
#Path("/api")
public class HelloRestService {
#Inject
HelloBean helloBean;
#GET
#Path("/echo/{name}")
public Response echo(#PathParam("name") String name) {
return Response.status(200).entity(helloBean.sayHello(name)).build();
}
}
Stateless EJB:
#Stateless
public class HelloStatelessBean implements HelloBean {
// Injected MyBatis mapper (dao)
#Inject
private EmployeeMapper employeeMapper;
#Override
public Employee getEmployeeById(final Long id) {
return employeeMapper.getEmployeeById(id);
}
#Override
public ArrayList<Employee> getEmployee() {
return employeeMapper.getAllEmployee();
}
/**
* Echo.
*/
#Override
public String sayHello(String name) {
return String.format("Hello %s! This is Ejb :)", name);
}
}
MyBatis mapper interface:
#Dependent
#Mapper
public interface EmployeeMapper {
#Select("select * from EMPLOYEE where id = #{id}")
Employee getEmployeeById(Long id);
#Select("select * from EMPLOYEE")
ArrayList<Employee> getAllEmployee();
}
I have a nice junit test for testing my MyBatis mapper. It works fine. The next step is that I would like to write a test for my jersey rest class. This is what I have:
public class HelloRestServiceTest extends JerseyTest {
#Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(HelloRestService.class) {
{
register(new HelloStatelessBean());
register(Mockito.mock(EmployeeMapper.class));
}
};
}
#Test
public void echo() throws Exception {
Response response = target("/api/echo/John").request().get();
Assert.assertEquals(200, response.getStatus());
Assert.assertNotNull(response.getEntity());
}
}
But this test throws an exception:
org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider a.b.HelloStatelessBean registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider a.b.HelloStatelessBean will be ignored.
org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider a.b.EmployeeMapper$$EnhancerByMockitoWithCGLIB$$ee86c913 registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider a.b.EmployeeMapper$$EnhancerByMockitoWithCGLIB$$ee86c913 will be ignored.
A MultiException has 1 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EmployeeMapper,parent=HelloStatelessBean,qualifiers={},position=-1,optional=false,self=false,unqualified=null,52451302)
MultiException stack 1 of 1
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EmployeeMapper,parent=HelloStatelessBean,qualifiers={},position=-1,optional=false,self=false,unqualified=null,52451302)
What is the proper way to mock MyBatis mapper with mockito?
UPDATE 1
EJB test works like a charm:
#RunWith(MockitoJUnitRunner.class)
public class HelloStatelessBeanTest {
private static SqlSessionFactory sqlSessionFactory;
#Spy
private HelloBean helloBean = new HelloStatelessBean();
#Test
public void sayHello() throws Exception {
String actual = helloBean.sayHello("Pear");
System.out.println(actual);
}
#Test
public void testGetEmployeeById() {
// create an SqlSessionFactory
try (Reader reader = Resources.getResourceAsReader("mybatis-configuration.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployeeById(1l);
Assert.assertNotNull(employee);
Assert.assertNotNull(employee.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
But it does not work with jersey. I have tried it this way, but I get same exception then before:
public class HelloRestServiceTest extends JerseyTest {
#Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(HelloRestService.class) {
{
try (Reader reader = Resources.getResourceAsReader("configuration.xml")) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
register(new HelloStatelessBean());
register(mapper, EmployeeMapper.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
}
#Test
public void echo() throws Exception {
Response response = target("/api/echo/Arnold").request().get();
Assert.assertEquals(200, response.getStatus());
Assert.assertNotNull(response.getEntity());
}
}
Any idea?