How to get better coverage in Junit test with two arguments - java

I'm writing Junit tests and I'm getting only partial coverage. I'd like to get a better coverage percentage with the following method.
#Getter
#Setter
private String sessionId = "";
#Service
public class Service {
private final WebClient webClient;
public Flux<String> getIdsList(String sessionId, String query) throws RuntimeException {
LinkedMultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("q", query);
logger.info("Retrieved ID list.");
return webClient
.post()
.uri("/query")
.header("Authorization", sessionId)
.body(BodyInserters.fromMultipartData(map))
.retrieve()
.bodyToMono(ProductListResponse.class)
.doOnSuccess(this::checkVaultResponseErrors)
.flatMapIterable(res -> {
List<String> idList = new ArrayList<>();
res.data.forEach(id -> Objects.requireNonNull(idList).add(id.id));
if (idList.isEmpty())
logger.info("No new products for update or insert found.");
else
logger.info(String.format("Found %s new / updated products.", String.valueOf(idList.size())));
return idList;
})
.doOnError(throwable -> {
String warnMessage = "Unable to get products ids: " + throwable.getMessage();
logger.warn(warnMessage);
})
.delayElements(Duration.ofMillis(420));
}
Here's a ProductListResponse class
#JsonIgnoreProperties(ignoreUnknown = true)
public class ProductListResponse extends VaultResponse {
#JsonProperty("data")
public List<ProductIDs> data;
public static class ProductIDs {
#JsonProperty("id")
public String id;
}
}
VaultResponse class looks like this:
#Getter
#JsonInclude(JsonInclude.Include.NON_NULL)
public class VaultResponse {
#JsonProperty("responseStatus")
public ResponseStatus responseStatus;
#JsonProperty("errors")
public List<Error> errors;
}
Here's my test.
#Test
void should_get_ids_list() {
String body = "{ \"key\" : \"value\"}";
var uriMock = Mockito.mock(WebClient.RequestHeadersUriSpec.class);
var headersSpecMock = Mockito.mock(WebClient.RequestHeadersSpec.class);
var responseSpecMock = Mockito.mock(WebClient.ResponseSpec.class);
webClient = WebClient.builder()
.exchangeFunction(clientRequest ->
Mono.just(ClientResponse.create(HttpStatus.OK)
.header("content-type", "application/json")
.body(body)
.build())
).build();
when(uriMock.uri(ArgumentMatchers.<String>notNull())).thenReturn(headersSpecMock);
when(headersSpecMock.header(notNull(), notNull())).thenReturn(headersSpecMock);
when(headersSpecMock.retrieve()).thenReturn(responseSpecMock);
when(responseSpecMock.bodyToMono(ArgumentMatchers.<Class<String>>notNull()))
.thenReturn(Mono.just(body));
I'm getting only partial coverage, and I was unable to get into the .flatMapIterable part of the tested method. Does anyone have an idea how I can improve my test?

Related

Unable to make graphQL query mutation for File Upload when using #GrpahQLScalar File file

Not able to create graph QL query mutation for file upload using #GraphQLScalar File when trying with this query getting 501 internal server error This is my graph QL query for file upload.
Please help here with your suggestions and whomsoever knows can reply with their answer as soon as possible.
{"query" : "mutation ($jbpTemplate:JbpTemplateInput,$jbpFile:FileScalar){uploadJbpPlan(jbpTemplate:{jbpId:"123345",jbpYear:2022,fileName:"ayush.xlsx",jbpFile:$jbpFile}) {jbplist{name,notes,type,startDate,dueDate,owners {emailId,firstName},phase,rowError},majorErrors}}","variables":{"jbpTemplate":{jbpId:"123345",jbpYear:2022}}}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#ToString
public class JbpTemplateData {
private List<JbpPlan> jbplist;
private boolean hasError;
private List<String> majorErrors;
private JbpTemplate template;
}
#GraphQLApi
#Component
public class JbpTemplateResolver {
#Autowired
private JbpTemplateService templateService;
#LogExecutionTime
#GraphQLMutation(name = "uploadJBPFile")
public JbpTemplateData uploadJbpFile(#GraphQLArgumen`(name = "jbpTemplate") JbpTemplate jbpTemplate) throws IOException {
return templateService.uploadJbpPlanFile(jbpTemplate);
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#ToString
public class JbpTemplate {
private String jbpId;
private String fileName;
#GraphQLScalar
private File jbpFile;
private int jbpYear;
private String accountId;
private String subAccountId;
private List<JBPActivity> actvitiyList;
public JbpTemplate(JbpTemplate temp) {
this.jbpId = temp.getJbpId();
this.jbpFile = temp.getJbpFile();
this.fileName = temp.getFileName();
this.actvitiyList = temp.getActvitiyList();
}
}
#RestController
#CrossOrigin
public class Retail360GraphQLController extends GraphQLController<NativeWebRequest> {
#Autowired
public Retail360GraphQLController(GraphQL graphQL, GraphQLMvcExecutor executor) {
super(graphQL, executor);
}
/**
* The Request contains the following parts: operations: JSON String with the
* GQL Query map: Maps the multipart files to the variables of the GQL Query
*/
#PostMapping(value = "/retail360", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE })
#ResponseBody
public Object executeMultipartPost(#RequestParam("operations") String operations,
#RequestParam("map") String map,
MultipartHttpServletRequest multiPartRequest,
NativeWebRequest webRequest) throws IOException, ServletException {
GraphQLRequest graphQLRequest = new ObjectMapper().readerFor(GraphQLRequest.class).readValue(operations);
Map<String, ArrayList<String>> fileMap = new ObjectMapper().readerFor(Map.class).readValue(map);
mapRequestFilesToVariables(multiPartRequest, graphQLRequest, fileMap);
return this.executeJsonPost(graphQLRequest, new GraphQLRequest(null, null, null, null), webRequest);
}
private void mapRequestFilesToVariables(MultipartHttpServletRequest multiPartRequest,
GraphQLRequest graphQLRequest,
Map<String, ArrayList<String>> fileMap) throws IOException, ServletException {
for (var pair : fileMap.entrySet()) {
String targetVariable = "jbpTemplate";
if (graphQLRequest.getVariables().containsKey(targetVariable)) {
Part correspondingFile = multiPartRequest.getPart(pair.getKey());
String filename = correspondingFile.getSubmittedFileName();
File file = Files.write(Files.createTempFile(filename.substring(0, filename.lastIndexOf(".")), filename.substring(filename.lastIndexOf("."))), correspondingFile.getInputStream().readAllBytes()).toFile();
Map map = (HashMap) graphQLRequest.getVariables().get("jbpTemplate");
map.put("jbpFile", file);
}
}
}
}

How to GET data using RestTemplate exchange?

I'm currently sending a GET request which is returning a null body in the response.
#Service
public class CarService {
private RestTemplate restTemplate;
private final String url = "url";
private final String accessToken = "x";
#Autowired
public CarService () throws URISyntaxException {
restTemplate = new RestTemplate();
}
public void fetchCars() throws URISyntaxException {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(accessToken);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
ResponseEntity<CarList> carList = restTemplate.exchange
(RequestEntity.get(new URI(url)).headers(headers).build(), CarList.class);
}
}
The CarList.class looks like this:
#JsonIgnoreProperties(ignoreUnknown = true)
public class CarList{
private List<Car> carList;
public CarList(List<Car> carList) {
this.carList= carList;
}
public CarList() {
}
public List<Car> getCarList() {
return carList;
}
#Override
public String toString() {
return "CarList{" +
"carList=" + carList +
'}';
}
}
The response in Postman looks like this:
{
"cars": [
{
"carUid": "aaaa-ccc-dd-cc-ee",
"model": "hyundai",
"price": 20000,
"soldAt": "2021-09-24T22:10:15.307Z"
}
]
}
Am I missing something?
It's the first time I try to consume from a client, so take in consideration the most basic things that I might be missing.
I've already tested the GET request in Postman with the given accessToken and it's working fine.
You carList object is private, try providing a setter method for this object and if the contract is fine, the deserialization will work fine.
#JsonIgnoreProperties(ignoreUnknown = true)
public class CarList{
private List<Car> carList;
public CarList(List<Car> carList) {
this.carList= carList;
}
public CarList() {
}
public List<Car> getCarList() {
return carList;
}
public setCarList(List<Car> carList) {
this.carList= carList;
}
#Override
public String toString() {
return "CarList{" +
"carList=" + carList +
'}';
}
}

Using kafka as messaging queue

I am trying to use Kafka as a messaging queue in my controller (so task):
#RequestMapping("users")
#RestController
public class UserController extends BaseController {
private static final String KAFKA_TOPIC = "virto_users";
#Autowired
private KafkaTemplate<String, String> mKafkaTemplate;
#PutMapping(value = "{id}")
public ResponseEntity<String> put(#PathVariable UUID id,
#RequestBody String profile) {
String url = ServerConfig.USERS_HOST + "/users/" + id;
ResponseEntity<String> entity = request.put(url, profile);
HttpStatus status = entity.getStatusCode();
if (status == HttpStatus.CREATED || status == HttpStatus.NO_CONTENT) {
return entity;
}
JSONObject json = new JSONObject();
json.put("id", id);
json.put("profile", profile);
sendMessage(json.toString());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
public void sendMessage(String msg) {
mKafkaTemplate.send(KAFKA_TOPIC, msg);
}
#KafkaListener(topics = KAFKA_TOPIC, groupId = KafkaConfig.GROUP_ID)
public void listen(String message) {
JSONObject json = new JSONObject(message);
UUID id = UUID.fromString(json.getString("id"));
String profile = json.getString("profile");
put(id, profile);
}
}
If my inner service is available and returned CREATED or NO_CONTENT then all is right and I can return result else I need to return the NO_CONTENT status and put this message in my queue to try again until it will be processed. I made it like above but it doesn't look like a good solution, imho. I wanted to ask just for a some advice how can I improve this solution or that it's normal.

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

using Swagger #ApiResponse responseContainer not working when code is 400

In Swagger Java API, when I use a responsecontainer="List" (Or "Set") with a code=400, I am not getting the model of the response on Swagger-GUI. I am just getting Array[Object].
Here is the concrete case:
#CrossOrigin
#RestController
#RequestMapping(value = "/api")
#Loggable(prepend = true, trim = false)
public class ConfigResource {
private final ConfigResourceDelegate delegate;
#Inject
public ConfigResource(final ConfigResourceDelegate delegate) {
this.delegate = delegate;
}
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "Set", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<?> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}
}
Here is the Rejection Class:
public class Rejection {
private Long id;
private RejectionDTO rejection;
private String originMessage;
public Rejection() {
}
public Long getId() {
return id;
}
public RejectionDTO getRejection() {
return rejection;
}
public String getOriginMessage() {
return originMessage;
}
public void setId(Long id) {
this.id = id;
}
public void setRejection(RejectionDTO rejection) {
this.rejection = rejection;
}
public void setOriginMessage(String originMessage) {
this.originMessage = originMessage;
}
}
So normally i'am supposed to have this model between [] in the swagger UI. However, I am getting Array[Object]:
See screen capture
To make your example work, you need to change your return value from wildcard, ResponseEntity<?>, to a concrete class, ResponseEntity<List<Rejection>>. Also, you need to change responseContainer to a List from Set.
#RequestMapping(
value = "/v1/config",
method = PUT,
consumes = APPLICATION_JSON_UTF8_VALUE,
produces = APPLICATION_JSON_UTF8_VALUE
)
#ApiResponses(value = {#ApiResponse(code=202,message = "ACCEPTED" ),
#ApiResponse(code=200,response = Rejection.class, responseContainer
= "List", message = "BAD_REQUEST"),
#ApiResponse(code=500, message = "INTERNAL_SERVER_ERROR")})
public ResponseEntity<List<Rejection>> putConfig(final #RequestBody ConfigDto
configDto){
return delegate.putConfig(riskConfigDto);
}

Categories

Resources