I'm new in JUnit with Mock. This is actual working but I notice receiving null value in TEST using Map<String, Object>
The reason for this what if their is condition must not null. Someday I'm going to need this.
What I did was since Map<String, Object> can't be implemented in headers.setAll() because it need Map<String, String> so i convert it.
#RestController
public class TestController {
#GetMapping("/test")
public String testAuth(#RequestParam String name, #RequestHeader Map<String, Object> head) {
try {
String conv = String.valueOf( head.get("head"));
return name + " "+ conv ;
} catch (Exception e) {
return e.getMessage();
}
}
}
Test
#WebMvcTest(controllers = TestController.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testControllerHeader() throws Exception {
Map<String, Object> headerMap = new HashMap<String, Object>();
Map<String, String> headObjectToString = headerMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));
HttpHeaders headers = new HttpHeaders();
headers.setAll(headObjectToString);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.get("/test?name=justin")
.headers(headers)
.accept(MediaType.APPLICATION_JSON)
).andExpect(status().isOk()).andReturn();
//i make it error just to see the body
assertEquals(result.getResponse().getStatus(), 201);
//this the original
//assertEquals(result.getResponse().getStatus(), 200);
}
}
Body = justin null
MockHttpServletRequest:
HTTP Method = GET
Request URI = /test
Parameters = {name=[justin]}
Headers = [Accept:"application/json"]
Body = null
Session Attrs = {}
Handler:
Type = com.mock.TestingMokito.Controller.TestController
Method = com.mock.TestingMokito.Controller.TestController#testAuth(String, Map)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json", Content-Length:"10"]
Content type = application/json
Body = justin null
Forwarded URL = null
Redirected URL = null
Cookies = []
You're passing null in the header, that's why it is returning null.
In your test class, add some data in the headerMap before converting it to a string.
#WebMvcTest(controllers = TestController.class)
public class ControllerTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testControllerHeader() throws Exception {
Map<String, Object> headerMap = new HashMap<String, Object>();
headerMap.put("head",new String("This was the bug"));
Map<String, String> headObjectToString = headerMap.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));
HttpHeaders headers = new HttpHeaders();
headers.setAll(headObjectToString);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders
.get("/test?name=justin")
.headers(headers)
.accept(MediaType.APPLICATION_JSON)
).andExpect(status().isOk()).andReturn();
//i make it error just to see the body
assertEquals(result.getResponse().getStatus(), 201);
//this the original
//assertEquals(result.getResponse().getStatus(), 200);
}
}
Related
We have a put api that will update an assignment based on its id. As we should be cleaning up the data after a test, our assignment id would change after the original one is deleted, so we're trying to dynamically inject that into the body for the request from the provider side. However, we seem to be perhaps missing something here as it's not updating correctly and the request is still being triggered with the id set as the example.
This is the provider class:
#Slf4j
#Provider("Assignments API")
#Consumer("LTI-AGS-Tool")
//#PactBroker(url = BROKER_PACT_URL, authentication = #PactBrokerAuth(token = "${pactbroker.auth.token}"))
#VerificationReports(value = {"console", "markdown"}, reportDir = "target/pacts")
class PactProviderLTIAGSIT {
private HashMap<String, String> headers = new HashMap<>();
private String updateAssignmentId;
private final String SERVICE_TOKEN = "myToken";
#BeforeEach
void createTeacherAssignment() {
String assignmentBody = createBodyStringForStudentAssignmentSetup();
assignmentBody = assignmentBody.replace("CPWAG", "OTHER_TEXT_RESOURCE");
headers.put("Content-Type", "application/json");
headers.put("Authorization", "myToken");
RequestSpecification rq = Util.getRequestSpecification().baseUri(baseAssignmentUrl).headers(headers);
Response response = rq.body(assignmentBody).post();
assertEquals(201, response.getStatusCode());
updateAssignmentId = response.jsonPath().get("assignments[0].refId");
log.info("assignment id is " + updateAssignmentId);
}
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void pactTestTemplate(PactVerificationContext context, HttpRequest request) {
request.addHeader("Authorization", SERVICE_TOKEN);
logCurlFromPact(context, request);
context.verifyInteraction();
}
#BeforeEach
void before(PactVerificationContext context) {
context.setTarget(new HttpsTestTarget(BASE_PACT_TEACHER_ASSIGNMENTS_URL, 443, ""));
}
#State("Scoring info is passed between ags-tool and assignmentapi")
Map<String, Object> getScoringInfo() {
Map<String, Object> map = new HashMap<>();
map.put("assignmentId", updateAssignmentId);
return map;
}
}
And here the consumer contract:
#ExtendWith(PactConsumerTestExt.class)
class PactConsumerSendScoreIT {
private final Map<String, String> headers = new HashMap<>();
private final String path = "/v5/assignmentStatus/update";
#Pact(provider = PACT_PROVIDER, consumer = PACT_CONSUMER)
public RequestResponsePact scoreConsumerPact(PactDslWithProvider builder) {
headers.put("Content-Type", "application/json");
//Body given and returned
DslPart body = new PactDslJsonBody()
.valueFromProviderState("assignmentId", "assignmentId", "c1ef3bbf-55a2-4638-8f93-22b2916fe085")
.stringType("timestamp", DateTime.now().plusHours(3).toString())
.decimalType("scoreGiven", 75.00)
.decimalType("scoreMaximum", 100.00)
.stringType("comment", "Good work!")
.stringType("status", "IN_PROGRESS")
.stringType("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085")
.close();
return builder
.given("Scoring info is passed between ags-tool and assignmentapi")
.uponReceiving("Scoring info is passed between ags-tool and assignmentapi")
.path(path)
.method("POST")
.body(body)
.headers(headers)
.willRespondWith()
.status(201)
.body(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "scoreConsumerPact", providerName = PACT_PROVIDER, port = "8080", pactVersion = PactSpecVersion.V3)
void runTest(MockServer mockServer) {
String updateAssignmentId = "c2ef3bbf-55a2-4638-8f93-22b2916fe085";
HashMap<String, Object> map = new HashMap<>();
map.put("timestamp", DateTime.now().plusHours(3).toString());
map.put("scoreGiven", 75.00);
map.put("scoreMaximum", 100.00);
map.put("comment", "Good work!");
map.put("status", "IN_PROGRESS");
map.put("userId", "c2ef3bbf-55a2-4638-8f93-22b2916fe085");
map.put("assignmentId", updateAssignmentId);
//Mock url
RequestSpecification rq = Util.getRequestSpecification().baseUri(mockServer.getUrl()).headers(headers);
Response response = rq.body(map)
.post(path);
assertEquals(201, response.getStatusCode());
}
}
Thank you.
We figured out that the expression needed is to include the ${} (at least if it's a string).
Once we updated it to valueFromProviderState("assignmentId", "${assignmentId}", "c1ef3bbf-55a2-4638-8f93-22b2916fe085") it seemed to work.
Today I'm dealing with an issue creating a client to consume a restful service with a Bearer JWT. After appying the swagger codegen maven plugin I got the following GET operation what I need to consume in my new service:
public String getRelationByCucoIdUsingGET(Integer cucoId, String GS_AUTH_TOKEN) throws RestClientException {
Object postBody = null;
// verify the required parameter 'cucoId' is set
if (cucoId == null) {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'cucoId' when calling getRelationByCucoIdUsingGET");
}
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("cucoId", cucoId);
String path = UriComponentsBuilder.fromPath("/getRelationByCucoId/{cucoId}").buildAndExpand(uriVariables).toUriString();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
if (GS_AUTH_TOKEN != null)
headerParams.add("GS-AUTH-TOKEN", apiClient.parameterToString(GS_AUTH_TOKEN));
final String[] accepts = {
"application/json"
};
final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
final String[] contentTypes = { };
final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);
String[] authNames = new String[] { };
ParameterizedTypeReference<String> returnType = new ParameterizedTypeReference<String>() {};
return apiClient.invokeAPI(path, HttpMethod.GET, queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType);
}
The model class is as follow:
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class CuCoPerson {
private Integer cucoId;
private List<CustomerRelation> customers;
}
And last, I have done this service but it doesnt work and I don't know how to create the controller to use this service.
#Service
public class CustomerRelationService {
RestControllerApi restControllerApi = new RestControllerApi();
public void getCustomers(Integer cudoId, Principal auth) {
restControllerApi.getRelationByCucoIdUsingGET(cudoId, auth.getName());
}
Thank you for clicking here.
I have an JSON REST API (providing by Directus CMS). All API responses contains a json object with a "data" attribute containing what I want.
{
"data": {
"id": 1,
"status": "published",
"sort": null,
"user_created": "5a91c184-908d-465e-a7d5-4b648029bbe0",
"date_created": "2022-04-26T09:43:37.000Z",
"user_updated": "5a91c184-908d-465e-a7d5-4b648029bbe0",
"date_updated": "2022-05-30T14:23:50.000Z",
"Titre": "Réseaux Sociaux",
"Description": "Retrouvez les dernières news en direct sur nos réseaux sociaux!",
"Lien": "https://www.instagram.com/univlorraine/",
"ImageArrierePlan": "f23ffd53-7244-4439-a8cf-41bd0fd3aa72",
"Erreur_Bloc": null
}
}
This data attribute can be a object or a list of objects depending the request.
I have a Java Spring application with a service consuming the API. I'm using RestTemplate with exchange method.
public Object callAPI(String url, HttpMethod httpMethod, Object body, MultiValueMap<String, String> headers, Class<?> classe) {
final RestTemplate rt = new RestTemplate();
try {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
rt.setRequestFactory(requestFactory);
final HttpEntity<?> request = new HttpEntity<>(body, headers);
final ResponseEntity<?> response = rt.exchange(url, httpMethod, request, classe);
if (response.getStatusCode().equals(HttpStatus.OK)) {
return response.getBody();
}
else return response.getStatusCode();
} catch (final Exception e) {
System.out.println(e);
return null;
}
}
In the exchange method I pass an existing class to directly link response data with the provided class.
The probleme is that I have this data attribute which prevents me from linking the data.
Does anyone have a solution to this probleme please?
----UPDATE----
Thanks to the response of AlbiKai, I created a generic Wrapper class :
public class Wrapper<T> {
private T data;
public void set(T data) {
this.data = data;
}
public T get() {
return data;
}
}
I then tried to put this Wrapper in the exchange :
public <classe> Object callAPI(String url, HttpMethod httpMethod, Object body, MultiValueMap<String, String> headers, Class<?> classe) {
final RestTemplate rt = new RestTemplate();
try {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
rt.setRequestFactory(requestFactory);
final HttpEntity<?> request = new HttpEntity<>(body, headers);
final ResponseEntity<?> response = rt.exchange(url, httpMethod, request, Wrapper<classe>.class);
But I get the error "Cannot select from parameterized type" on the Wrapper :/
You can create a wrapper class that match the json response : an object with only one attribute named "data" type of desire final class (or a list) and use it in the exchange method.
public class wrapper {
YourClass data;
}
I gave up with the Wrapper etc...
I just pass a String class and work with it in my controllers to delete this "data" property and map the string with a class.
Service :
public String callAPI(String url, HttpMethod httpMethod, Object body, MultiValueMap<String, String> headers) {
final RestTemplate rt = new RestTemplate();
try {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
rt.setRequestFactory(requestFactory);
final HttpEntity<?> request = new HttpEntity<>(body, headers);
final ResponseEntity<String> response = rt.exchange(url, httpMethod, request, String.class);
if (response.getStatusCode().equals(HttpStatus.OK)) {
return response.getBody();
}
else return response.getStatusCode().toString();
} catch (final Exception e) {
System.out.println(e);
return null;
}
}
One controller :
public List<BlocInformation> getBlocInformation() {
String url = "http://localhost:8055/items/bloc_information/?fields=*,Erreur_Bloc.*";
final RestAPIService blocService = new RestAPIService();
String response = blocService.callAPI(url, HttpMethod.GET, null, null);
if (response != null) {
String result = response.substring(8, response.length() - 1);
ObjectMapper mapper = new ObjectMapper();
List<BlocInformation> blocInformationList = null;
try {
blocInformationList = Arrays.asList(mapper.readValue(result, BlocInformation[].class));
} catch (IOException e) {
e.printStackTrace();
}
return blocInformationList;
}
return null;
}
I have the following test code where I'm testing a Pageable endpoint that list all entries for student.
#Autowired
private MockMvc mockMvc;
#MockBean
private StudentRepository studentRepository;
private PageableHandlerMethodArgumentResolver pageableArgumentResolver = new PageableHandlerMethodArgumentResolver();
#BeforeEach
public void init() {
mockMvc = MockMvcBuilders.standaloneSetup(new StudentEndpoint(studentRepository))
.setCustomArgumentResolvers(pageableArgumentResolver)
.build();
}
#Test
#WithMockUser(username = "xx", password = "xx", roles = "USER")
public void whenListStudentUsingCorrectStudentUsernameAndPassword_thenReturnStatusCode200 () throws Exception {
List<Student> students = asList(new Student(1L, "Legolas", "legolas#lotr.com"),
new Student(2L, "Aragorn", "aragorn#lotr.com"));
when(studentRepository.findAll()).thenReturn(students);
mockMvc.perform(get("http://localhost:8080/v1/protected/students/"))
.andExpect(status().isOk())
.andDo(print());
verify(studentRepository, times(1)).findAll();
}
The problem here is that the verify(studentRepository, times(1)).findAll(); doesn't work because MockHttpServletResponse is returning a null Body.
Thats my endpoint:
#GetMapping(path = "protected/students")
public ResponseEntity<?> listAll (Pageable pageable) {
return new ResponseEntity<>(studentDAO.findAll(pageable), HttpStatus.OK);
}
And my log:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null Redirected URL = null
Cookies = []
Argument(s) are different! Wanted:
br.com.devdojo.repository.StudentRepository#0 bean.findAll(
);
-> at br.com.devdojo.TestingTestTech.whenListStudentUsingCorrectStudentUsernameAndPassword_thenReturnStatusCode200(TestingTestTech.java:68)
Actual invocations have different arguments:
br.com.devdojo.repository.StudentRepository#0 bean.findAll(
Page request [number: 0, size 20, sort: UNSORTED]
);
Could someone please help with the right way to test pageable response? Thanks.
Finally, I found how to fix it.
You just need to pass a Pageable object as parameter to your findAll method that returns a Pageable JSON.
Thats my new working code:
Page<Student> pagedStudents = new PageImpl(students);
when(studentRepository.findAll(isA(Pageable.class))).thenReturn(pagedStudents);
mockMvc.perform(get("http://localhost:8080/v1/protected/students/"))
.andExpect(status().isOk())
.andDo(print());
verify(studentRepository).findAll(isA(Pageable.class));
And the MockHttpServletResponse:
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json"]
Content type = application/json
Body = {"content":[{"id":1,"name":"Legolas","email":"legolas#lotr.com"},{"id":2,"name":"Aragorn","email":"aragorn#lotr.com"}],"pageable":"INSTANCE","totalElements":2,"totalPages":1,"last":true,"size":2,"number":0,"sort":{"sorted":false,"unsorted":true,"empty":true},"first":true,"numberOfElements":2,"empty":false}
Forwarded URL = null
Redirected URL = null
Cookies = []
I'm doing simple unit test by using mockMvc with springboot. SampleCtrl.java is origin source code and SampleCtrlTest.java is test source code. There are 3 mocks injected to SampleCtrl and they are working well except SampleService class. As you can see, SampleService wrapped with try-catch block. So, I have no idea how to call the method in mock class to get return or throw exception in test source code.
I removed try-catch block in origin code, also given() method in test code. It worked well. That's why I think the try-catch block is undoubted reason to make error.
SampleCtrl.java
#PostMapping(path = "/save", consumes = "application/json")
#ResponseBody
public ResponseEntity<Map<String, Object>> setEmp(#RequestBody
List<EmpSaveVo> vos) {
result.clear();
try {
msg = service.setEmp(vos);
} catch (Exception e) {
msg = e.getMessage();
}
result = messageTrans.getMapLang(msg);
return messageReturn.getRestResp(result, msg);
}
SampleCtrlTest.java
private MockMvc mockMvc;
#Mock
private SampleService service;
#Mock
private MessageTrans messageTrans;
#Mock
private MessageReturn messageReturn;
#InjectMocks
private SampleCtrl sampleCtrl;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(sampleCtrl).build();
}
#Test
public void givenEmployeeDataWhenPostEmpSave() throws Exception {
List<EmpSaveVo> empSaveVos = new ArrayList<>();
EmpSaveVo empSaveVo = new EmpSaveVo();
empSaveVo.setEmployeeId(100L);
empSaveVo.set_status((long) Status.Modified.getStatus());
empSaveVos.add(empSaveVo);
Gson gson = new Gson();
String element = gson.toJson(empSaveVos);
String msg = "Test";
Map<String, Object> result = new HashMap<>();
result.put("message", msg);
given(service.setEmp(empSaveVos)).willThrow(new Exception());
given(messageTrans.getMapLang(msg)).willReturn(result);
given(messageReturn.getRestResp(any(), anyString()))
.willReturn(new ResponseEntity<Map<String, Object>>(result, HttpStatus.OK));
mockMvc.perform(post("/api/emp/save")
.content(element)
.contentType(MediaType.APPLICATION_JSON)).andDo(print())
.andExpect(status().isOk());
}
Consequently, I want to see this console log.
MockHttpServletResponse:
Status = 200
Error message = null
Headers = [Content-Type:"application/json;charset=UTF-8"]
Content type = application/json;charset=UTF-8
Body = {"message":"Test"}
Forwarded URL = null
Redirected URL = null
Cookies = []