Handling MethodArgumentNotValidException in Junit testcase? - java

I am using spring MVC testing: In my test case, I am passing an invalid Bar object(age with zero). The MethodArgumentNotValidException is being thrown, but it is nested inside a NestedServletException. Is there anyway to throw the MethodArgumentNotValidException exception from the controller through an existing/custom HandlerExceptionResolver, so that my current test case checkHit2 passes?
Controller:
#RequestMapping(value="/test", method = RequestMethod.POST, headers="Accept=application/json")
#ResponseBody
public Bar getTables(#Valid #RequestBody Bar id) {
return id;
}
TestCase
#Before
public void setUp() {
mockMvc = standaloneSetup(excelFileUploader).setHandlerExceptionResolvers(new SimpleMappingExceptionResolver()).build();
}
#Test(expected=MethodArgumentNotValidException.class)
public void checkHit2() throws Exception {
Bar b = new Bar(0, "Sfd");
mockMvc.perform(
post("/excel/tablesDetail").contentType(
MediaType.APPLICATION_JSON).content(
TestUtil.convertObjectToJsonBytes(b)));
Bar
public class Bar {
#JsonProperty("age")
#Min(value =1)
private int age;
public Bar(int age, String name) {
super();
this.age = age;
this.name = name;
}
...
}
Junit output
java.lang.Exception: Unexpected exception,
expected<org.springframework.web.bind.MethodArgumentNotValidException> but
was<org.springframework.web.util.NestedServletException>

I had similar issue and I fixed it extending my exception class from NestedServletException. For example:
#RequestMapping(value = "/updateForm/{roleID}", method = RequestMethod.GET)
public String updateForm(#PathVariable Long roleID, Model model, HttpSession session) throws ElementNotFoundException {
Role role = roleService.findOne(roleID);
if (role == null) {
throw new ElementNotFoundException("Role");
}
...
}
And my exception looks like:
public class ElementNotFoundException extends NestedServletException {
private static final long serialVersionUID = 2689075086409560459L;
private String typeElement;
public ElementNotFoundException(String typeElement) {
super(typeElement);
this.typeElement = typeElement;
}
public String getTypeElement() {
return typeElement;
}
}
So my test is:
#Test(expected = ElementNotFoundException.class)
public void updateForm_elementNotFound_Test() throws Exception {
String roleID = "1";
Mockito.when(roleService.findOne(Long.valueOf(roleID))).thenReturn(null);
mockMvc.perform(get("/role/updateForm/" + roleID)).andExpect(status().isOk()).andExpect(view().name("exception/elementNotFound"));
}

Related

unit Test cases for spring boot application

I have written 1 unit Test for 1 single public method and need help from other methods of customer controller which I can refer to and write for other controllers and services.
CustomerController
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api/v1/")
public class CustomerController {
private static final Logger log = LoggerFactory.getLogger(CustomerController.class);
#Autowired
private CustomerRepository customerRepository;
public List<Customer> getAllcustomers(){
return customerRepository.findAll();
}
**public Customer createcustomer(#RequestBody Customer customer) {
log.info("inside customer add ***********");
return customerRepository.save(customer);
}**
**public ResponseEntity<Customer> getcustomerById(#PathVariable Long id) {
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
return ResponseEntity.ok(customer);
}**
**public ResponseEntity<Customer> updatecustomer(#PathVariable Long id, #RequestBody Customer customerDetails){
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
customer.setFullName(customerDetails.getFullName());
customer.setPhoneNumber(customerDetails.getPhoneNumber());
customer.setPhone2(customerDetails.getPhone2());
customer.setDistrict(customerDetails.getDistrict());
Customer updatedcustomer = customerRepository.save(customer);
return ResponseEntity.ok(updatedcustomer);
}**
**public ResponseEntity<Map<String, Boolean>> deletecustomer(#PathVariable Long id){
Customer customer = customerRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("customer not exist with id :" + id));
customerRepository.delete(customer);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return ResponseEntity.ok(response);
}**
}
Below is the Customer Model - which I am using
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String fullName;
private int phoneNumber;
private int phone2;
private String email;
private String district;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public int getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(int phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
UnitTest for Customer.
#ExtendWith(MockitoExtension.class)
public class CustomerControllerTest {
#Mock
CustomerRepository customerRepository;
#InjectMocks
CustomerController customerController;
#Test
public void testGetAllcustomers() {
when(customerRepository.findAll()).thenReturn(getCusts());
List<Customer> res = customerController.getAllcustomers();
assertEquals(res.size(),1);
}
public List<Customer> getCusts(){
List<Customer> custs = new ArrayList<>();
Customer c = new Customer();
c.setFullName("Dinga");
custs.add(c);
return custs;
}
}
Like the above Unit Test cases, I need other methods also. Marked in Bold for which I need Unit Test cases.
For testing a rest controller, it's recommended to use mockMvc. This acts like a rest client, but it does not actually start the server. Instead it uses the spring classes to call your code in almost the same way as if it were processing a real HTTP request. MoockMvc will perform the conversion of your data to Json and retrieve a Json result. Here's the official documentation, with some examples on how to use it: springdoc. Using ObjectMapper (or JacksonTester, which uses an ObjectMapper) you can deserialize the respone into objects.
#WebMvcTest(CustomerController.class)
#ComponentScan("com.your.base.package")
class ControllerTest{
#MockBean
private CustomerRepository customerRepository;
#Autowired
private MockMvc mockMvc;
#Test
void test1(){
when(customerRepository.findAll()).thenReturn(<whatever you want>)
// call mockMvc
this.mockMvc.perform(get("/")).andDo(print())
.andExpect(status().isOk());
}
}

Spring Boot Application- Ambiguous mapping Cannot map method

i am creating a simple spring boot project when i run the project ran into the problem with Spring Boot Ambiguous mapping. Cannot map method. i have create two various controller those are student and course controller.i completly attached the error below.
i attached the full
**gub link here** https://github.com/raguram1986/SpringSecuritys
Full Error i attached below
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'studentController' method
com.example.studentmanagement.Controller.StudentController#saveStudent(Student)
to {POST [/save]}: There is already 'courseController' bean method
com.example.studentmanagement.Controller.CourseController#saveCourse(Course) mapped.
Controller
#Controller
public class StudentController {
#Autowired
private StudentService service;
#GetMapping("/Student")
public String viewHomePage(Model model) {
List<Student> liststudent = service.listAll();
// model.addAttribute("liststudent", liststudent);
System.out.print("Get / ");
return "Student";
}
#GetMapping("/addStudent")
public String add(Model model) {
List<Student> liststudent = service.listAll();
model.addAttribute("liststudent", liststudent);
model.addAttribute("student", new Student());
return "addstudent";
}
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
#RequestMapping("/edit/{id}")
public ModelAndView showEditStudentPage(#PathVariable(name = "id") int id) {
ModelAndView mav = new ModelAndView("addstudent");
Student std = service.get(id);
mav.addObject("student", std);
return mav;
}
#RequestMapping("/delete/{id}")
public String deleteStudentPage(#PathVariable(name = "id") int id) {
service.delete(id);
return "Student";
}
Student
#Entity
public class Student {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
private String stname;
private String course;
private int fee;
public Student() {
}
public Student(Long id, String stname, String course, int fee) {
this.id = id;
this.stname = stname;
this.course = course;
this.fee = fee;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStname() {
return stname;
}
public void setStname(String stname) {
this.stname = stname;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public int getFee() {
return fee;
}
public void setFee(int fee) {
this.fee = fee;
}
StudentRepository
#Repository
public interface StudentRepository extends JpaRepository<Student, Long>{
}
Service
#Service
public class StudentService
{
#Autowired
private StudentRepository repo;
public List<Student> listAll() {
return repo.findAll();
}
public void save(Student std) {
repo.save(std);
}
public Student get(long id) {
return repo.findById(id).get();
}
public void delete(long id) {
repo.deleteById(id);
}
}
In your StudentController you have the endpoint /save
#RequestMapping(value = "/save", method = RequestMethod.POST)
public String saveStudent(#ModelAttribute("student") Student std) {
service.save(std);
return "Student";
}
But you haven't included the CourseController class in your question, which is mentioned in the error.
If you have defined an endpoint /save in that CourseController, then you have to rename it. Otherwise, when you invoke /save which controller needs to be invoked cannot be determined.
Add #RequestMapping above StudentController as below
#Controller
#RequestMapping("/students")
public class StudentController {
...
}
You are getting exception because there is already a mapping defined for path
/save
without being any controller mapping, so the first with root mapping is considered, but the next time it encounters same mapping it is already registered hence it is complaining. For clarity i'd suggest to add #RequestMapping to CourseController as well.
So now your course controller also becomes:
#Controller
#RequestMapping("/courses")
public class CourseController {
...
}
The best practice is to always add a request mapping at class level as well, like in your case, add a mapping like "/students" for SutdentController and "/course" for CourseController itself and then all other methods will be under that i.e. "/student/save" and then you will not face this issue anymore.

HTTP 401 Unauthorized error occurs in Spring Boot test

Entity class
#Getter
#NoArgsConstructor
#Entity
public class Posts {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 500, nullable = false)
private String title;
#Column(columnDefinition = "TEXT", nullable = false)
private String content;
private String author;
#Builder
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}
}
Test code
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsAPIControllerTest {
#LocalServerPort
private int port;
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private PostsRepository postsRepository; // PostsRepository extends JpaRepository<Posts, Long>
#After
public void tearDown() throws Exception {
postsRepository.deleteAll();
}
#Test
public void posts_save() throws Exception {
String title = "title";
String content = "content";
PostsSaveRequestDTO requestDTO = PostsSaveRequestDTO.builder()
.title(title)
.content(content)
.author("author")
.build();
String url = "http://localhost:" + port + "/api/v1/posts";
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDTO, Long.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);
}
}
Controller
#RequiredArgsConstructor
#RestController
public class PostsAPIController {
private final PostsService postsService;
#PostMapping("/api/v1/posts")
public Long save(#RequestBody PostsSaveRequestDTO requestDTO) {
return postsService.save(requestDTO);
}
#PutMapping("/api/v1/posts/{id}")
public Long update(#PathVariable Long id, #RequestBody PostsUpdateRequestDTO requestDTO) {
return postsService.update(id, requestDTO);
}
#GetMapping("/api/v1/posts/{id}")
public PostsResponseDTO findById(#PathVariable Long id) {
return postsService.findById(id);
}
}
I made a sample Spring Boot test code that updates DB, but test fails with following error message if I execute the code. I already defined spring.security.user.name and spring.security.user.password to application.properties file.
What is the problem? I tried to reload after removing testImplementation 'org.springframework.security:spring-security-test' from build.gradle, but nothing has changed.
Expecting:
<401 UNAUTHORIZED>
to be equal to:
<200 OK>
but was not.
There are multiple ways to mock the security using #WithMockUser, #WithAnonymousUser, #WithUserDetails, #WithSecurityContext. You can use these annotations with #Test method
You may change the roles as required in the project. You may like to explore more details here
#Test
#WithMockUser(username="admin",roles="ADMIN")
public void posts_save() throws Exception {
String title = "title";
String content = "content";
PostsSaveRequestDTO requestDTO = PostsSaveRequestDTO.builder()
.title(title)
.content(content)
.author("author")
.build();
String url = "http://localhost:" + port + "/api/v1/posts";
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDTO, Long.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);
List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);

How to write Junit test cases for spring boot application?

I have to write some junit test cases to check entity. I'm using postgres as my database.
My entity class
#Entity
#Table(name = "display")
public class Display {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String title;
private String group;
public Display() {
}
public Display(Long id, String title, String grp) {
this.id = id;
this.title= title;
this.group= grp;
}
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return this.id;
}
public void setGroup(String id) {
this.group = id;
}
public String getGroup() {
return this.group;
}
public void settitle(String title) {
this.title = title;
}
public String gettitle() {
return this.title;
}
}
My repository
#Repository
public interface DisplayRepository extends CrudRepository<Display, Long> {
}
Interface
public interface IDisplayService {
List<Display> findAll();
}
Service class
#Service
public class DisplayService implements IDisplayService {
#Autowired
private DisplayRepository repository;
#Override
public List<Display> findAll() {
List<Display> d = (List<Display>) repository.findAll();
return d;
}
}
I tried writing junit test cases but I get Could'nt load Application. Whats the right way to write junit test cases for this?
This is the test case I wrote for service
folder : test/java/example/demo/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value);
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
in test/java/example/demo/resources
conn.properties
id=2
Its returning 0 for value
Whats the issue?
Thanks
I have managed to make your code to work. I will post only the changed classes:
The interface:
public interface DisplayRepository extends CrudRepository<Display, Long> {
Optional<Display> findByTitle(String name);
}
The test class:
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
#DataJpaTest
public class DisplayRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private DisplayRepository productRespository;
#Before()
public void setUp(){
Display m = new Display();
// m.setId(2L); // The ID is autogenerated; can retrieve it from the persistAndFlush result
m.setCategory("Group1");
m.setTitle("Product2");
testEntityManager.persistAndFlush(m);
}
#Test
public void whenFindByName_thenReturnProduct() {
// when
Display product = productRespository.findByTitle("Product2").orElseThrow(() -> new RuntimeException("Product not found"));
// then
assertThat(product.getTitle()).isEqualTo("Product2");
}
#Test
public void whenFindAll_thenReturnProductList() {
// when
List<Display> products = (List<Display>) productRespository.findAll();
// then
assertThat(products).hasSize(1);
}
}
When trying to run the code you provided, there were a few issues:
you were using the reserved word group as a field in the Display class. Because of this, Hibernate couldn't create the table, so I renamed it to category.
there was a compilation issue because the method findByName wasn't defined in the repository; also, there was no field name in the Display class to which the mapping to be made; because of this, I've added the method findByTitle because it's an existing field and it seemed to match the value you queried in the test method.
because the ID field is autogenerated, the test setup() failed when persisting the Display.
If you want to use #Mock for mocking classes, you must call:
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
You can then mock responses as usual: Mockito.when(DisplayReps.findByTitle("A")).thenReturn(Optional.of(new Display(2L, "ALFA", "GRP1")));

Current group in class level bean validation

I have some group validations in a bean:
#FichaValida(groups={Ficha.DatosGenerales.class, Ficha.Economia.class})
public class Ficha {
public interface DatosGenerales{}
public interface Documentos{}
public interface Ubigeo{}
public interface Economia{}
#NotEmpty(groups = {DatosGenerales.class})
String apPrimer;
#NotEmpty(groups = {DatosGenerales.class})
String apSegundo;
#NotEmpty(groups = {DatosGenerales.class})
String preNombres;
#NotEmpty(groups = {Documentos.class})
String tiDocumento;
#NotEmpty(groups = {Documentos.class})
String nuDocumento;
#NotEmpty(groups = {Ubigeo.class})
String deDepartamento;
#NotEmpty(groups = {Ubigeo.class})
String deProvincia;
#NotEmpty(groups = {Ubigeo.class})
String deDistrito;
#NotEmpty(groups = {Economia.class})
String nuIngreso;
#NotEmpty(groups = {Economia.class})
String nuGasto;
//members, setters and getters
}
And these methods:
#RequestMapping(value = "datos-generales.do", method = RequestMethod.POST)
public String datosGenerales(
#Validated({Ficha.DatosGenerales.class}) Ficha ficha,
BindingResult bindingResult){
}
#RequestMapping(value = "documentos.do", method = RequestMethod.POST)
public String documentos(
#Validated({Ficha.Documentos.class}) Ficha ficha,
BindingResult bindingResult){
}
#RequestMapping(value = "economia.do", method = RequestMethod.POST)
public String economia(
#Validated({Ficha.Economia.class}) Ficha ficha,
BindingResult bindingResult){
}
How I can know in the validator class which group is currently validating?
public class FichaValidator implements ConstraintValidator<FichaValida, Ficha> {
private FichaValida fichaValida;
public void initialize(FichaValida fichaValida) {
this.fichaValida = fichaValida;
}
public boolean isValid(Ficha ficha, ConstraintValidatorContext constraintValidatorContext) {
/*
if(Ficha.DatosGenerales.class==...){
//some validations
}else if(Ficha.Economia.class==...){
//some validations
}
*/
return true;
}
}
I need to do validations with the members according the current group validation, by example if DatosGenerales group is validating, only use members related to it.
public boolean isValid(Ficha ficha, ConstraintValidatorContext constraintValidatorContext) {
if(Ficha.DatosGenerales.class==this.fichaValida.value()){
//some validations
}else if(Ficha.Economia.class==this.fichaValida.value()){
//some validations
}
return true;
}

Categories

Resources