Getting 204 for spring-boot integration test - spring-boot

I am just trying to write a simple integration test for my spring-boot application
#SpringBootTest(classes = ZooApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class AnimalControllerIntTest {
#LocalServerPort
int port;
#Autowired
TestRestTemplate restTemplate;
String localHost = "http://localhost:";
#Test
public void tesAddAnimal(){
AnimalDto animalDto = createAnimalDtoObject();
ResponseEntity<AnimalDto> response = this.restTemplate.postForEntity(this.localHost+port+"/animal/add", animalDto, AnimalDto.class);
assertEquals(1L, response.getBody().getId());
}
private AnimalDto createAnimalDtoObject() {
AnimalDto animalDto = new AnimalDto();
RoomDto roomDto = new RoomDto();
roomDto.setRoomSize(25l);
roomDto.setTitle("Green");
RoomDto roomDto1 = new RoomDto();
roomDto1.setRoomSize(10l);
roomDto1.setTitle("Blue");
animalDto.setRoomDto(roomDto);
//animalDto.setFavoriteRooms();
FavoriteDto f1 = new FavoriteDto();
f1.setAnimalDto(animalDto);
f1.setRoomDto(roomDto);
FavoriteDto f2 = new FavoriteDto();
f2.setAnimalDto(animalDto);
f2.setRoomDto(roomDto1);
Set<FavoriteDto> favSet = new HashSet<>();
favSet.add(f2);
favSet.add(f1);
animalDto.setTitle("dog");
//animalDto.setFavoriteRooms(favSet);
return animalDto;
}
}
and below is my original implementation
#RestController
#AllArgsConstructor
#FieldDefaults(level = AccessLevel.PRIVATE)
#RequestMapping("/animal")
public class AnimalController {
AnimalService animalService;
#PostMapping("/add")
public AnimalDto addAnimal(#Valid #RequestBody AnimalDto animalDto) throws InvalidRoomDetailException {
return animalService.add(animalDto);
}
}
AnimalService
#Transactional
public AnimalDto add(AnimalDto animalDto) throws InvalidRoomDetailException {
Animal animal = mapper.animalDtoToAnimal(animalDto);
isValidAnimal(animal);
animal = animalRepository.save(animal);
log.debug("animal object saved:{}",animal);
List<Favorite> favoriteRoom = addFavortieRoom(animalDto, animal);
if(animalDto.getRoomDto() != null) {
Room room = addAnimalToRoom(animal, animalDto);
animal.setRoom(room);
animal = animalRepository.save(animal);
}
//animal = animalRepository.save(animal);
if(favoriteRoom != null)
animal.setFavoriteRooms(new HashSet<>(favoriteRoom));
return mapper.animalToAnimalDto(animal);
}
private List<Favorite> addFavortieRoom(AnimalDto animalDto, Animal animal) throws InvalidRoomDetailException {
Set<Favorite> favoriteRooms = mapper.favoriteDtosToFavorite(animalDto.getFavoriteRooms());
if(favoriteRooms != null ){
for(Favorite favoriteRoom: favoriteRooms){
favoriteRoom.setAnimal(animal);
if(favoriteRoom.getRooms() != null) {
Optional<Room> roomExist = roomRepository.findById(favoriteRoom.getRooms().getId());
if (roomExist.isEmpty())
throw new InvalidRoomDetailException("this favorite room does exist:" + favoriteRoom.getRooms().getTitle());
favoriteRoom.setRooms(roomExist.get());
}
}
return favoriteRepository.saveAll(favoriteRooms);
}
return null;
}
Dto class:
#Data
#FieldDefaults(level = AccessLevel.PRIVATE)
#NoArgsConstructor
#AllArgsConstructor
public class AnimalDto {
Long id;
String title;
LocalDateTime located;
String type;
Long preference;
#JsonManagedReference("animalFavoriteRoom")
#ToString.Exclude
Set<FavoriteDto> favoriteRooms;
//#JsonBackReference("animalRoom")
RoomDto roomDto;
}
when I am ran actually this is working but my integration test is giving me 204 HttpStatus as it does not have any body. can you please let me know whre I am going wrong?

Related

WebFlux API-Layer Test returns 404

I'm trying to get started with Spring WebFlux with Spring Boot 3.0
I'm Building a Person API with an open api generator.
The Application runs and gives the expected results when it is tested manually.
But I'm not able to get the API layer unit tested.
This is my Test Class
#WebFluxTest(controllers = {PersonApiController.class})
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {
#MockBean
private PersonService personService;
#Autowired
private WebTestClient client;
#ParameterizedTest
#CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
void retrieve_a_name(String id, String name) {
when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));
client.get()
.uri(uriBuilder -> uriBuilder
.path("/persons/{id}")
.build(id))
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.name").isEqualTo(name);
}
This is my Controller Class
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-
09T09:14:36.692713900+01:00[Europe/Vienna]")
#Controller
#RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {
private final PersonApiDelegate delegate;
public PersonApiController(#Autowired(required = false) PersonApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
}
#Override
public PersonApiDelegate getDelegate() {
return delegate;
}
}
The API interface:
#Tag(
name = "Person",
description = "the Person API"
)
public interface PersonApi {
default PersonApiDelegate getDelegate() {
return new PersonApiDelegate() {
};
}
#Operation(
operationId = "findPersonById",
summary = "Find Person by ID",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operation",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.GET},
value = {"/persons/{id}"},
produces = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> findPersonById(#Parameter(name = "id",description = "Person ID",required = true) #PathVariable("id") Integer id, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().findPersonById(id, exchange);
}
#Operation(
operationId = "savePerson",
summary = "Creates a new Person",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operatoin",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.POST},
value = {"/persons"},
produces = {"application/json"},
consumes = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> savePerson(#Parameter(name = "PersonData",description = "") #RequestBody(required = false) Mono<PersonData> personData, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().savePerson(personData, exchange);
}
}
and finally my delegate impl:
#Service
public class PersonDelegateImpl implements PersonApiDelegate {
public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());
private final PersonService service;
private final PersonMapper mapper;
public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
this.service = service;
this.mapper = mapper;
}
public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
.onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));
}
#Override
public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
return toResponseEntity(personDataByID.map(mapper::map));
}
#Override
public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
return PersonApiDelegate.super.savePerson(personData, exchange);
If I run the test class I always get:
< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]
{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}
I have tried to change the Context Configuration but I did not get it to work.
I found the Problem, changing the Test Config to :
#WebFluxTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class, PersonApiController.class, PersonDelegateImpl.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
Solved my Problem.
The Controller bean was not recognized. I had to add PersonApiCrontroller and PersonDelegateImpl to the Context Config. i then removed the PersonApiController from the #WebFluxTest annotation.

How to use List in another function

--------------------------------------Table 1--------------------------------------
#Entity
#Table(name = "student_details")
public class StudentDetails {
#Id
private Long studentId;
private String firstName;
private String lastName;
private String fatherName;
// with getter and setters
}
-------------------------------------Table 2---------------------------------
#Entity
#Table(name = "student_marks")
public class StudentMarks {
#Id
private Long studentId;
private int Maths;
private int English;
private int Computer;
// with getter and setters
}
------------------------------------class which combine these two table------------------
public class StudentReportData {
#Id
private Long studentId;
private String studentName
private String fatherName;
private int Maths;
private int English;
private int Computer;
//with getter and setters
}
-----------------------------------Service Class---------------------------
#Service
public class StudentService {
public ApiResponce<List<StudentReportData>> getStudentData() {
ApiResponce<List<StudentReportData>> responce = new ApiResponce();
Status status = new Status();
status setSuccess(true);
try {
List<StudentReportData> studentList = new ArrayList<StudentReportData>();
List<StudentMarks> findAll = studentMarksRepository.findAll();
for(StudentMarks sm : findAll) {
StudentReportData studentData = new StudentReportData();
BeanUtils.copyProperties(sm, studentData);
StudentDetails studentDetailsData = studentDetailsRepository.findByStudentId(sm.getStudentId());
studentData.setStudentName(studentDetailsData.getFirstName()+" "+studentDetailsData.getLastName());
studentList.add(studentData);
}
response.setData(studentList);
} catch (Exception ex) {
ex.printStackTrace();
ErrorData errorData = new ErrorData();
errorData.setErrorCode("ERR003");
errorData.setErrorMessage("Internal Server Error");
status.setErrorData(errorData);
status.setSuccess(false);
}
response.setStatus(status);
return response;
}
public ByteArrayInputStream load() {
ByteArrayInputStream in = ExcelHelper.studentreportToExcel(studentList);
return in;
}
}
I want to pass this studentList as an argument to the function studentReportToExcel from the function load. How can I get that List Inside the load function.
Add it as a class attribute, something like:
#Service
public class StudentService {
private List<StudentReportData> studentList; // class attribute to add
public ApiResponce<List<StudentReportData>> getStudentData() {
ApiResponce<List<StudentReportData>> responce = new ApiResponce();
Status status = new Status();
status setSuccess(true);
try {
studentList = new ArrayList<StudentReportData>(); // line changed
List<StudentMarks> findAll = studentMarksRepository.findAll();
for(StudentMarks sm : findAll) {
StudentReportData studentData = new StudentReportData();
BeanUtils.copyProperties(sm, studentData);
StudentDetails studentDetailsData = studentDetailsRepository.findByStudentId(sm.getStudentId());
studentData.setStudentName(studentDetailsData.getFirstName()+" "+studentDetailsData.getLastName());
studentList.add(studentData);
}
response.setData(studentList);
} catch (Exception ex) {
ex.printStackTrace();
ErrorData errorData = new ErrorData();
errorData.setErrorCode("ERR003");
errorData.setErrorMessage("Internal Server Error");
status.setErrorData(errorData);
status.setSuccess(false);
}
response.setStatus(status);
return response;
}
public ByteArrayInputStream load() {
ByteArrayInputStream in = ExcelHelper.studentreportToExcel(studentList);
return in;
}
}

Spring Controller Test: Postman vs JUnit - Field error request rejected value [null]

I'm a beginner on Spring framework, trying to test the Controller.
The funny thing is, using Postman, I got the correct response, but not in JUnit where receive Actual :400 (bad request) instead of Expected :200.
This is due to empty field passengerCount because appears null. The class of the request is different of the response. This latter doesn't have a field for the passenger.
Controller
#Validated
#RestController
#RequestMapping("flights")
public class BusyFlightsController {
CrazyAirDatabase crazyAirService;
#Autowired
public BusyFlightsController(CrazyAirDatabase crazyAirService) {
this.crazyAirService = new CrazyAirDatabase();
}
#RequestMapping(value = "/crazy-air-response", method = RequestMethod.GET, produces = "application/json")
public List<CrazyAirResponse> getCrazyAirResponse(
#Valid CrazyAirRequest crazyAirRequest,
#RequestParam("origin") String origin,
#RequestParam("destination") String destination,
#RequestParam("departureDate") String departureDate,
#RequestParam("returnDate") String returnDate,
#RequestParam("passengerCount") int passengerCount
) {
crazyAirRequest = new CrazyAirRequest(origin, destination, departureDate, returnDate,
passengerCount);
return crazyAirService.getCrazyAirResponse(crazyAirRequest);
}
}
CrazyAirRequest class
public class CrazyAirRequest {
#IATACodeConstraint
private String origin;
#IATACodeConstraint
private String destination;
private String departureDate;
private String returnDate;
private int passengerCount;
public CrazyAirRequest(String origin, String destination, String departureDate,
String returnDate, int passengerCount) {
this.origin = origin;
this.destination = destination;
this.departureDate = departureDate;
this.returnDate = returnDate;
this.passengerCount = passengerCount;
}
// Getters
}
CrazyAirResponse class
public class CrazyAirResponse {
private String airline;
private double price;
private String cabinClass;
private String departureAirportCode;
private String destinationAirportCode;
private String departureDate;
private String arrivalDate;
public CrazyAirResponse(String airline, double price, String cabinClass, String departureAirportCode,
String destinationAirportCode, String departureDate, String arrivalDate) {
this.airline = airline;
this.price = price;
this.cabinClass = cabinClass;
this.departureAirportCode = departureAirportCode;
this.destinationAirportCode = destinationAirportCode;
this.departureDate = departureDate;
this.arrivalDate = arrivalDate;
}
// Getters
}
Repo CrazyAirDatabase
#Component
public class CrazyAirDatabase implements CrazyAirService {
List<CrazyAirResponse> list;
public CrazyAirDatabase() {
list = new ArrayList<>(
Arrays.asList(
new CrazyAirResponse("Ryanair", 125, "E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
}
#Override
public List<CrazyAirResponse> getCrazyAirResponse(CrazyAirRequest request) {
return list.stream()
.filter(t -> t.getDepartureAirportCode().equals(request.getOrigin()) &&
t.getDestinationAirportCode().equals(request.getDestination()) &&
t.getDepartureDate().equals(request.getDepartureDate()) &&
t.getArrivalDate().equals(request.getReturnDate())
)
.collect(Collectors.toList());
}
}
Test
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class BusyFlightsControllerTest {
#Autowired
MockMvc mockMvc;
#Mock
CrazyAirRequest crazyAirRequest;
#InjectMocks
private BusyFlightsController controller;
#Mock
CrazyAirService service;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
#Test
public void testino() throws Exception {
crazyAirRequest = new CrazyAirRequest("LHR",
"BRN", "2018-10-08", "2020-10-08", 120);
List<CrazyAirResponse> crazyAirResponse = Arrays.asList(new CrazyAirResponse("Ryanair", 125,
"E", "LHR",
"BRN", "2018-10-08", "2020-10-08")
);
when(service.getCrazyAirResponse(crazyAirRequest)).thenReturn(crazyAirResponse);
ObjectMapper objectMapper = new ObjectMapper();
String airplane = objectMapper.writeValueAsString(crazyAirResponse);
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
result.andExpect(status().isOk());
}
}
If I put this:
ResultActions result = mockMvc.perform(get("/flights/crazy-air-response?origin=LHR&destination=CTA&departureDate=some&returnDate=some&passengerCount=1")
.contentType(MediaType.APPLICATION_JSON)
.content(airplane)
);
Test is passed.
Then, need I perform Postman first, and after to copy and paste the query to pass the test?

How to invoke #Service from another project with REST call in Spring?

I need to use #Service from another Spring project. I will need a REST call to invoke it but how am I suppose to do it?
This is the service:
#Service
public class LocationsService implements ILocationsService {
private final Logger logger = LoggerFactory.getLogger("LocationsService");
private final ILocationRepository locationRepository;
private final IEvseRepository evseRepository;
private final IConnectorRepository connectorRepository;
#PersistenceContext
private EntityManager entityManager;
#Autowired
public LocationsService(ILocationRepository locationRepository, IEvseRepository evseRepository, IConnectorRepository connectorRepository, EntityManager entityManager) {
this.locationRepository = locationRepository;
this.evseRepository = evseRepository;
this.connectorRepository = connectorRepository;
this.entityManager = entityManager;
}
public Location getLocation(String countryCode, String partyId, String id) {
return locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
}
public Location deleteLocation(String countryCode, String partyId, String id) {
Location location = locationRepository.findByCountryCodeAndPartyIdAndId(countryCode, partyId, id);
if (location == null) {
logger.info("Location does not exist.");
return null;
}
locationRepository.deleteById(location.getLocId());
return location;
}
I need to call the service in this controller. The controller is in a different project:
#RestController
#RequestMapping(value = "/locations", produces = MediaType.APPLICATION_JSON_VALUE)
#Api(tags = "Locations management")
public class LocationController {
#Autowired
private LocationsService locationsService;
#RequestMapping(method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
#ApiOperation(value = "Get Locations", notes = "Get locations", nickname = "getLocations",
authorizations = #Authorization(value = "Bearer"))
public ResponseEntity<List<LocationDto>> getLocations() {
List<LocationDto> locations = new ArrayList<>();
return new ResponseEntity<>(locations, HttpStatus.OK);
}
}
I searched for solutions but found nothing helpful and will appreciate any help.
Thank you!

Problems with WebDataBinder and Set.Class

i am having trouble with binding my data from a form :
I have two class
#Entity
#Table(name = "ROLES")
public class Role implements GenericDomain {
private Long id;
private String code;
private String name;
private Set<Privilege> privileges = new HashSet<Privilege>(0);
public Role() {}
/* getter and setter*/
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "ROLES_PRIVILEGES"
, joinColumns = { #JoinColumn(name = "ROLE_ID") }
, inverseJoinColumns = { #JoinColumn(name = "PRIVILEGE_ID") }
)
public Set<Privilege> getPrivileges() {
return this.privileges;
}
public void setPrivileges(Set<Privilege> privileges) {
this.privileges = privileges;
}
/* overide of hascode, equals*/
}
And
#Entity
#Table(name = "PRIVILEGES")
public class Privilege implements GenericDomain {
private Long id;
private String code;
private Set<Role> roles = new HashSet<Role>(0);
public Privilege() {}
/* getter and setter*/
#ManyToMany(cascade=CascadeType.REFRESH, mappedBy="privileges")
public Set<Role> getRoles() {
return this.roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#Override
public String toString(){
return this.getCode() + this.getComment();
}
/*overide equals and hascode*/
and in my controller i have :
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "privileges", new CustomCollectionEditor(Set.class) {
#Override
protected Object convertElement(Object element) {
return (element == null)?null:privilegeService.getOne(Integer.parseInt((String)element));
}
});
}
#RequestMapping(value = "edit", method = RequestMethod.POST)
public String saveOldRole( #ModelAttribute("role") Role role
, BindingResult result
, ModelMap model
) {
validator.validate(role, result);
if (result.hasErrors()){
logger.error(result.getAllErrors());
model.addAllAttributes(result.getModel());
return "/admin/role/edit";
}
logger.info(role.getPrivileges());
Iterator p = role.getPrivileges().iterator();
while(p.hasNext()){
logger.info(p.next().getClass());
}
roleService.saveOrUpdate(role);
model.addAttribute("roles", roleService.getAll());
sessionStatus.setComplete();
return "redirect:/admin/role/list.do";
}
and my debug is
role.RoleController:93 - [[MANAGE_USERS], [MANAGE_ROLES]]
role.RoleController:96 - class java.util.LinkedHashSet
role.RoleController:96 - class java.util.LinkedHashSet
22:29:44,915 ERROR tomcat-http--7 property.BasicPropertyAccessor:194 - IllegalArgumentException in class: com.stunaz.domain.Privilege, getter method of property: id
I dont understand why at 96, the class type is java.util.LinkedHashSet, i thought it should be Privileges.
I dont understand why my role.getPrivileges() is a Set of Set, it should be a Set of Privilege.
Of course at saveOrUpdate am getting an error.
finaly!!!
there were no bug at all!
i updated my spring jar from 3.0.5.RELEASE to 3.1.0.M1, and voila : somthing stopped working with webdatabinder and CustomCollectionEditor.
i just rollback to 3.0.5.RELEASE and everything is fine.

Resources