WebMvcTest of #Controller with #RequestParam and status is not 200, but 400 - java

i try to write #WebMvcTest:
#ExtendWith(SpringExtension.class)
#WebMvcTest(AudienceController.class)
class LectureControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private LectureService lectureService;
#MockBean
private GroupService groupService;
#MockBean
private TeacherService teacherService;
#MockBean
private StudentService studentService;
#BeforeEach
public void init() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new LectureController(lectureService, groupService, teacherService, studentService)).build();
}
#Test
void whenGetRequest_thenReturnTTForTeacherPage() throws Exception {
Teacher teacher = new Teacher(1, "teacher", "first");
Subject subject = new Subject(1, "first");
LocalDateTime date = LocalDateTime.now();
Audience audience = new Audience(1, 100);
Group group = new Group(1, "group");
Lecture lecture = new Lecture(1, teacher, subject, Arrays.asList(group), date);
lecture.setAudience(audience);
when(teacherService.findOne(1)).thenReturn(teacher);
when(lectureService.findLecturesByTeacher(teacher, date, date)).thenReturn(Arrays.asList(lecture));
mockMvc.perform(get("/lecture/TTForTeacher/{id}", 1)
.param("startDay", date.toString())
.param("endDay", date.toString()))
.andDo(print())
.andExpect(status().isOk())
.andExpect(view().name("lecture/TTForTeacher"))
.andExpect(forwardedUrl("lecture/TTForTeacher"))
.andExpect(model().attribute("lectures", hasSize(1)))
.andExpect(model().attribute("lectures", hasItem(
allOf(
hasProperty("id", is(1)),
hasProperty("teacher", is(teacher)),
hasProperty("subject", is(subject)),
hasProperty("date", is(date)),
hasProperty("audience", is(audience)),
hasProperty("groups", is(Arrays.asList(group)))
)
)));
verify(lectureService, times(1)).findLecturesByTeacher(teacher, date, date);
}
for my controller:
#Controller
#RequestMapping("/lecture")
public class LectureController {
private LectureService lectureService;
private GroupService groupService;
private TeacherService teacherService;
private StudentService studentService;
public LectureController(LectureService lectureService, GroupService groupService, TeacherService teacherService,
StudentService studentService) {
this.lectureService = lectureService;
this.groupService = groupService;
this.teacherService = teacherService;
this.studentService = studentService;
}
#GetMapping("/TTForTeacher/{id}")
public String getTTForTeacher(#PathVariable("id") int id, #RequestParam(value = "startDay") LocalDateTime startDay,
#RequestParam(value = "endDay") LocalDateTime endDay, Model model) {
model.addAttribute("lectures",
lectureService.findLecturesByTeacher(teacherService.findOne(id), startDay, endDay));
return "lecture/TTForTeacher";
}
But it has failures: "Status expected:<200> but was:<400>"
and this in console:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /lecture/TTForTeacher/1
Parameters = {startDay=[10.05.2020, 10:00], endDay=[31.05.2020, 20:00]}
Headers = []
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.foxminded.university.controller.LectureController
Method = com.foxminded.university.controller.LectureController#getTTForTeacher(int, LocalDateTime, LocalDateTime, Model)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
I tried not use .param("day", date.toString), to set string of several formats to this param, to use not .param, but i have this exception, can u help me with it. All other test of other methods are successful, but there are no #RequestParam in those methods. So how can i test methods like this

The controller is missing logic to parse data. Spring by default does not know how to convert the string to datetime object. There are 2 ways of solving this, one solution could be read date as String instead of LocalDate and then parse String to get the startDay and endDay, but there is a better way using #DateTimeFormat https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/format/annotation/DateTimeFormat.html
Controller method becomes like this
public String getTTForTeacher(#PathVariable("id") int id, #RequestParam(value = "startDay") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDay,
#RequestParam(value = "endDay") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDay) {
Test mockMvc can be left as is since you are doing LocalDateTime.now().toString()

Related

checkmarx medium severity: May unintentionally allow setting the value of method in the object

I am getting below medium vulnerability highlighted in checkmarx:
The rModificationRequest at r-config\com\mycompapi\RController.java in
line# may unintentionally allow setting the value of saveAndFlush in
modifyR, in the object r-config\com\mycompservices\RService.java at
line#.
#RestController
#RequestMapping(path = "/api/v1/r", produces = MediaType.APPLICATION_JSON_VALUE)
#Api(tags = "R", value = "Endpoints for managing all the operations related to r")
#Slf4j
#Validated
public class RController {
private final RService rService;
private final ModelMapper modelMapper;
#Autowired
public RController(final RService rService,
final ModelMapper modelMapper) {
this.rService = rService;
this.modelMapper = modelMapper;
}
#ApiOperation(value = "Modify r information", nickname = "modifyR")
#PatchMapping
#ResponseStatus(HttpStatus.OK)
public RResponse modifyRInfo(
#RequestParam(name = "r-name") #NotBlank
#Size(max = 256, message = "r name should have less than or equals to {max} characters") final String rName,
#Valid #RequestBody RModificationRequest rModificationRequest) {
final RModificationDto rModificationDto = modelMapper.map(rModificationRequest,
RModificationDto.class);
final R r = rService.modifyR(rName, rModificationDto);
return modelMapper.map(r, RResponse.class);
}
}
#Service
public class RService {
private final RRepository rRepository;
#Autowired
public RService(final RRepository rRepository) {
this.rRepository = rRepository;
}
#Transactional
#PublishNotification(operationType = OperationType.MODIFY)
public R modifyR(final String rName, final RModificationDto rModificationDto) {
final R r = findByRName(rName);
final R modifiedR = RServiceHelper.getModifiedR(r, rModificationDto);
rRepository.saveAndFlush(modifiedR);
return modifiedR;
}
What to do here or is it false positive? I don't see any comment also of what to do like sonar-cube scans have or may be its somewhere I don't know - I am new to checkmarx.

How to return 400 status in model validation spring boot

I want to test my StudentDTO :
#Entity
#ToString
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class StudentDTO {
#Id
private int studentId;
#NotNull
#Size(min=2,max=30,message = "Name should consist of 2 to 30 symbols!")
private String studentName;
#NotNull
#Size(min = 2, max = 30,message = "Surname should consist of 2 to 30 symbols!")
private String studentSurname;
#NotNull
#Min(value = 10,message = "Student age should be more than 10!")
private int studentAge;
#NotNull
#Min(value = 1900,message = "Entry year should be more than 1900!")
#Max(value=2021,message = "Entry year should be less than 2021!")
private int entryYear;
#NotNull
#Min(value = 2020,message = "Graduate year should be not less than 2020!")
private int graduateYear;
#NotNull
#Size(min = 3,message = "Faculty name should consist of minimum 3 symbols!")
private String facultyName;
#NotNull
#Size(min = 4,message = "Group name should consist of 4 symbols!")
#Size(max = 4)
private String groupName;
}
Method for testing in StudentController :
#PostMapping("successStudentAddition")
public String addStudent(#ModelAttribute("student") #Valid StudentDTO studentDTO, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute(STUDENT_MODEL, studentDTO);
return "/studentViews/addStudent";
}
Student student = new Student(studentDTO.getStudentId(), studentDTO.getStudentName(), studentDTO.getStudentSurname(),
studentDTO.getStudentAge(), studentDTO.getEntryYear(), studentDTO.getGraduateYear(), studentDTO.getFacultyName(),
groupService.getGroupIdByName(studentDTO.getGroupName()));
studentService.addStudent(student);
return "/studentViews/successStudentAddition";
}
I am trying to test in this way :
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = StudentController.class)
class StudentControllerTest {
#Autowired
private MockMvc mvc;
#Autowired
private ObjectMapper objectMapper;
#MockBean
private StudentController studentController;
#Test
void whenInputIsInvalid_thenReturnsStatus400() throws Exception {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setStudentId(0);
studentDTO.setStudentName("Sasha");
studentDTO.setStudentSurname("Georginia");
studentDTO.setStudentAge(0);
studentDTO.setEntryYear(5);
studentDTO.setGraduateYear(1);
studentDTO.setFacultyName("facop");
studentDTO.setGroupName("BIKS");
mvc.perform(post("/studentViews/successStudentAddition")
.accept(MediaType.TEXT_HTML))
.andExpect(status().isBadRequest())
.andExpect(model().attribute("student", studentDTO))
.andDo(print());
}
}
In my test I got 200 error, but I need to get 400 error with determined error above on the field from my StudentDTO.
e.g. if I pass studentAge = 5, I should to get 400 error and the message : Student age should be more than 10! like in the StudentDTO.
When you have such a condition, Spring will throw MethodArgumentNotValidException. To handle these exceptions you can write a class with #ControllerAdvice.
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<Error> invalidArgumentExceptionHandler(MethodArgumentNotValidException ex) {
// Instead of "/studentViews/successStudentAddition" you can return to some generic error page.
return new ResponseEntity<String>("/studentViews/successStudentAddition", HttpStatus.BAD_REQUEST);
}
}
I often turn to spring's org.springframework.http.ResponseEntity class.
#PostMapping("successStudentAddition")
public ResponseEntity<String> addStudent(#ModelAttribute("student") #Valid StudentDTO studentDTO, Errors errors, Model model) {
if (errors.hasErrors()) {
model.addAttribute(STUDENT_MODEL, studentDTO);
return new ResponseEntity<String>("/studentViews/addStudent", HttpStatus.BAD_REQUEST);
}
Student student = new Student(studentDTO.getStudentId(), studentDTO.getStudentName(), studentDTO.getStudentSurname(),
studentDTO.getStudentAge(), studentDTO.getEntryYear(), studentDTO.getGraduateYear(), studentDTO.getFacultyName(),
groupService.getGroupIdByName(studentDTO.getGroupName()));
studentService.addStudent(student);
return new ResponseEntity<String>("/studentViews/successStudentAddition", HttpStatus.Ok);
}

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);

MockMvc - calling a query with a complicated object

I want to send to the controller a complex object consisting of files and simple types.
public class ContributionNew<T extends MovieInfoDTO> {
private List<T> elementsToAdd;
private Map<Long, T> elementsToUpdate;
private Set<Long> idsToDelete;
private Set<String> sources;
private String comment;
}
public class Photo extends MovieInfoDTO {
private MultipartFile photo;
}
#PostMapping(value = "/{id}/contributions/photos")
#ResponseStatus(HttpStatus.CREATED)
public
ResponseEntity<Void> createPhotoContribution(
#ApiParam(value = "The movie ID", required = true)
#PathVariable("id") final Long id,
#ApiParam(value = "The contribution", required = true)
#RequestBody #Valid final ContributionNew<Photo> contribution
) {
I want to create a test to send an object, but I do not know how to finish it.
#Test
public void testCreatePhotoContribution() throws Exception {
ContributionNew<Photo> contribution = new ContributionNew<>();
MockMultipartFile multipartFile = new MockMultipartFile("photo", "C:\\Users\\Jonatan\\Pictures\\2.png",
"image/png", "Spring Framework".getBytes());
Photo.Builder photoBuilder = new Photo.Builder(
multipartFile
);
contribution.getElementsToAdd().add(photoBuilder.build());
mockMvc
.perform(post("/api/v1.0/movies/{id}/contributions/photos", 1)
.contentType(...)
.content(...))
.andExpect(status().isCreated());
}
I do not know how to send such an object as #ResuestBody? I do not know how to finish this test.
You can do something like this.
ObjectMapper = new ObjectMapper(); // You can also Autowire this
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mockMvc
.perform(post("/api/v1.0/movies/{id}/contributions/photos", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(contribution)))
.andExpect(status().isCreated());

Sending an object with files using the MockMvc test

I want to send an object to the controller that has several lists with files and several fields with plain text.
public class ContributionNew<T extends MovieInfoDTO> {
private List<T> elementsToAdd;
private Map<Long, T> elementsToUpdate;
private Set<Long> idsToDelete;
private Set<String> sources;
private String comment;
}
public class Photo extends MovieInfoDTO {
private MultipartFile photo;
}
#PostMapping(value = "/{id}/contributions/photos")
#ResponseStatus(HttpStatus.CREATED)
public
ResponseEntity<Void> createPhotoContribution(
#ApiParam(value = "The movie ID", required = true)
#PathVariable("id") final Long id,
#ApiParam(value = "The contribution", required = true)
#RequestBody #Valid final ContributionNew<Photo> contribution
) {
I want to create a test to send an object, but I do not know how to finish it.
#Test
public void testCreatePhotoContribution() throws Exception {
ContributionNew<Photo> contribution = new ContributionNew<>();
MockMultipartFile multipartFile = new MockMultipartFile("photo", "C:\\Users\\Jonatan\\Pictures\\2.png",
"image/png", "Spring Framework".getBytes());
Photo.Builder photoBuilder = new Photo.Builder(
multipartFile
);
contribution.getElementsToAdd().add(photoBuilder.build());
mockMvc
.perform(post("/api/v1.0/movies/{id}/contributions/photos", 1)
.contentType(...)
.content(...))
.andExpect(status().isCreated());
}
I do not know how to set the correct type for the transmitted data, set the content. Only tutorials about sending only files (not in objects) are available. But there are no guides where the file is one of the fields in the object. How to do it?

Categories

Resources