Controller:
#ApiOperation(value = " update record", response = APIResponse.class)
#ApiResponses(value = {#ApiResponse(code =200, message = "OK"),
#ApiResponses(value = {#ApiResponse(code =500, message = "Internal server error"),
#ApiResponses(value = {#ApiResponse(code =404, message = "NO_RECORD")})
#PutMapping(value = "/update/{id}")
#ResponseBody
public ResponseEntity<APIResponse> updateRecord(HttpServletRequest request, #RequestBody RecordDTO input, #PathVariable(value="id") int code){
APIResponse response = null;
try{
response = service.updateRecord(code, input);
}
catch(JSONException e){
log.error("Error Parsing JSON");
response = new APIResponse(HttpStatus.INTERNAL_SERVER_ERROR, ERROR_JSON_PARSING, ERROR);
}
return new ResponseEntity<>(response, HttpStatus.OK);
}
my test case foor controller:
#Test
public void update() throws Exception{
RecordDTO recordDto = new RecordDTO();
Object mapper = new ObjectMapper();
String value = mapper.writeValueAsString(StationDTO);
given(service.updateRecord(anyInt(), any(RecordDTO.class))).willThrow(JSONException.class);
mockMvc.perform(put(baseUrl + "/update/12")
.contentType(MediaType.APPLICATION_JSON).content(value))
.andExpect(status().isInternalservererror())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.status",Matchers.is("INTERNAL_SERVER_ERROR")))
.andExpect(jsonPath("$.message",Matchers.is("ERROR_JSON_PARSING")))
.andExpect(jsonPath("$.resposeStatus",Matchers.is("ERROR")));
APIResponse response = new APIResponse(HttpStatus.OK, SUCCESS, SUCCESS, null);
given(service.updateRecord(anyInt(), any(RecordDTO.class))).willReturn(response);
mockMvc.perform(put(baseUrl + "/update/12")
.contentType(MediaType.APPLICATION_JSON).content(value))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.status",Matchers.is("OK")))
.andExpect(jsonPath("$.message",Matchers.is("SUCCESS")))
.andExpect(jsonPath("$.resposeStatus",Matchers.is("SUCCESS")));
}
DTO:
public class RecordDTO{
private String id;
private String name;
private String number;
}
I am getting java.lang assertion error expected 500 but was 200. I don't what is wrong with the test case.. Is there any other way to write the test case? Also can you recommend any platform from where i can gain knowledge of how to write test cases then do comment down. Thanks for the help!
Seems like your mocked service is not injecting into your controller.
Alternative solution (I assume you use Spring-Boot):
DisableAutowireRequireInitializer. This will prevent to load all dependencies inside your Controller.
Create inside your ControllerTest inner class: private static ServiceImplMock entends ServiceImpl
Now, override updateRecord method inside ServiceMock to do your testing cases
#Override
public APIResponse updateRecord(int code, RecordDTO input) throws JSONException {
if(code == 12) throw new JSONException(...)
else your_business_logic
}
Now, add this ServiceImplMock into your #SpringBootTest
#SpringBootTest(classes = {
Controller.class,
ControllerTest.ServiceImplMock.class,
...
})
#AutoConfigureMockMvc
#ContextConfiguration( initializers = {DisableAutowireRequireInitializer.class })
class ControllerTest {
Now, your test cases should work (Remove given(...).willThrow(...); since we don't need it anymore)
Also can you recommend any platform from where i can gain knowledge of how to write test cases then do comment down
https://www.baeldung.com/junit
https://www.baeldung.com/spring-boot-testing
https://mkyong.com/spring-boot/spring-boot-junit-5-mockito/
Related
I am unable to get the mocked response from Feign Client. I provide below the code.
In the service class, it has been written like this.
public String getInfo(HttpServletRequest request, String id, String type) {
.... other code .....
try {
statusAsJsonString = myFeignClient.getStatus(cookie, id, type);
System.out.println("statusAsJsonString--------->"+statusAsJsonString);
ObjectNode node = new ObjectMapper().readValue(statusAsJsonString, ObjectNode.class);
if (node.has(CommonConstants.STATUS)) {
statusValue = node.get(CommonConstants.STATUS).asText();
}
} catch (FeignException fe) {
byte[] contents = fe.content();
String jsonContents = null;
if(contents != null) {
jsonContents = new String(contents);
}
statusValue = getErrorParsedStatusValue(jsonContents);
} catch (Exception ex) {
ex.printStackTrace();
}
log.debug("status: " + statusValue);
return statusValue;
}
In the unit test, I am trying to write in the following manner.
String responseBody = "[]";
when(myFeignClient.getStatus("cookievalue", "id", "SOme-Value")).thenReturn(responseBody);
I have also used, WireMock to achieve it.
wireMockServer.stubFor(WireMock.get("/rest/v1/somna/{id}/phase").withRequestBody(WireMock.equalToJson("{ \"name\": \"Phone\", \"initialStock\": 3}"))
.willReturn(WireMock.okJson(responseBody)));
The following piece of code is never covered and executed.
statusAsJsonString = myFeignClient.getStatus(cookie, id, type);
System.out.println("statusAsJsonString--------->"+statusAsJsonString);
Also the invocation of Feign client is inside a service method, first want to get the mocked result of that Feign client.
PLease help me.
I provide below my Feign CLient
#FeignClient(name = CommonConstants.FEIGN_CLIENT_NAME, url = "${feign.service.url}", primary = false)
public interface MyFeignClient {
#GetMapping(value = "/rest/v1/project/{id}/phaseName")
String getStatus(#RequestHeader("Cookie") String cookie,
#PathVariable("id") Stringid, #RequestParam("type") String type);
}
In my test class, I have added the followings.
#Autowired
private MyServiceImpl readyService = new MyServiceImpl();
#Mock
private MyFeignClient myFeignClient;
#ClassRule
public static WireMockServer wireMockServer = new WireMockServer(new WireMockConfiguration().port(8088));
#BeforeEach
void setUp() {
MockitoAnnotations.initMocks(this);
httpServletRequest = Mockito.mock(HttpServletRequest.class);
ReflectionTestUtils.setField(someService, "cookieName", "cookieName");
wireMockServer.start();
}
I'm new to testing and Mockito. However, not so new in working Spring.
I have a service layer implementation of exportResource(String id, String fileType, Class resourceClass) throws MyCustomEx1, MyCustomEx2. My assignment is to create a unit test with Mockito for exportResource() which is downloading a file directly from browser or throws exception if for some reason reaching to ResponseEntity return statement is not successfull.
Here is a rough overview of a service layer class where exportResource() is living;
#Service
#AllArgsConstructor
public class ExportImportServiceImpl implements ExportImportService {
private final FhirRepository fhirRepository;
private final CtsConfig ctsConfig;
#Override
public ResponseEntity<byte[]> exportResource(String id, String fileType, Class resourceClass) throws throws MyCustomEx1, MyCustomEx2 {
if (fileType == null ) throw new MyCustomEx1();
Bundle bundle = (Bundle) fhirRepository.resourcesWithCriterion(resourceClass, DaoConstants.ID, null).get();
if (bundle != null && bundle.hasEntry()) {
Optional<Bundle.BundleEntryComponent> resource =
bundle.getEntry()
.stream()
.filter(filter -> resourceClass.isInstance(filter.getResource()))
.findFirst();
if (resource.isPresent()) {
IBaseResource castedResource = (IBaseResource) resourceClass.cast(resource.get().getResource());
IParser parser = null;
MediaType mt = null;
if (fileType.equalsIgnoreCase("json")){
mt = MediaType.APPLICATION_JSON;
parser = fhirRepository.jsonParser();
} else if (fileType.equalsIgnoreCase("xml")){
mt = MediaType.APPLICATION_XML;
parser = fhirRepository.xmlParser();
} else throw new MyCustomEx1();
parser.setPrettyPrint(true);
var serializedResource = parser.encodeResourceToString(castedResource);
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,"attachment;" + "filename=" + resourceClass.getSimpleName() + "-" + castedResource.getId() + "." + fileType)
.contentType(mt)
.contentLength(serializedResource.getBytes(StandardCharsets.UTF_8).length)
.body(serializedResource.getBytes(StandardCharsets.UTF_8));
}
}
throw new MyCustomEx2();
}
//other implementations here...
}
I do understand (by wastching some tutorials etc) that we can use Mockito to mock external services/repositories, to verify if some method is getting called, to mock some return values etc. But I'm confused about if-else parts in my business logic-code. Namely, the part:
if (bundle != null && bundle.hasEntry()) {
Optional<Bundle.BundleEntryComponent> resource =
bundle.getEntry()
.stream()
.filter(filter -> resourceClass.isInstance(filter.getResource()))
.findFirst();
if (resource.isPresent()) {
IBaseResource castedResource = (IBaseResource) resourceClass.cast(resource.get().getResource());
IParser parser = null;
MediaType mt = null;
if (fileType.equalsIgnoreCase("json")){
mt = MediaType.APPLICATION_JSON;
parser = fhirRepository.jsonParser();
} else if (fileType.equalsIgnoreCase("xml")){
mt = MediaType.APPLICATION_XML;
parser = fhirRepository.xmlParser();
} else throw new ByException(ByErrorCode.ERR_BY_1013);
parser.setPrettyPrint(true);
var serializedResource = parser.encodeResourceToString(castedResource);
I don't understand how to write test to handle if-else part. I mean, isn't too simple to test something like that?
Here is what I (sadly) got:
#Test
void exportCodeSystem() throws Exception {
String id = "32";
verify(this.fhirRepository.resourcesWithCriterion(ValueSet.class, DaoConstants.ID, null));
Mockito.when(
controller.exportCodeSystem(Mockito.anyString(),Mockito.anyString())
).thenReturn(ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).build());
RequestBuilder request = MockMvcRequestBuilders.get("/url-to-controller")
.param("id","32")
.param("file-type", "json");
MvcResult result = mockMvc.perform(request).andReturn();
Assertions.assertFalse(result.getResponse().getStatus() == 200);
}
This throws some error. But that is not important as to how to write meaningful unit test in this case? Or even better, how to write code which can be easily unit-tested?
UPDATE 1
This is how my test looks now:
#Test
void exportCodeSystem() throws Exception {
CompletableFuture<Bundle> completedFuture = CompletableFuture.completedFuture(this.codeSystemBundle);
Mockito.when(this.fhirRepository.resourcesWithCriterion(ArgumentMatchers.<Class<CodeSystem>>any(),
ArgumentMatchers.<ICriterion<? extends IParam>>any(),
eq(null)))
.thenReturn(completedFuture);
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.
get("/code-system/export")
.param("id", "32").param("file-type", "json"))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().contentType("application/json")).andReturn();
Assertions.assertEquals(result.getResponse().getStatus(), HttpStatus.OK.value());
}
I keep getting NPE on the line where MvcResult result .. is. What I want to achive with test? I don't know to be honest. I'm trying to test in a some meaningful way, but I don't think I understand how to achieve that.
This is how controller looks like:
#AllArgsConstructor
#RestController
#RequestMapping("/code-system")
public class CodeSystemController {
private final ExportImportService exportImportService;
#GetMapping("/export")
public boolean exportCodeSystem(#RequestParam("id") String id, #RequestParam("file-type") String fileType) throws IOException, ByException{
return this.exportImportService.exportResource(id, fileType, CodeSystem.class);
}
}
What do you want to test? Would your test be unit test or integration test? Which bean do you want to unit test - controller or the service?
If you want to test the controller via mockMvc you can do this:
#Autowired
private MockMvc mockMvc;
#Autowired
ExportImportService exportImportService;
#Test
public void yourTest() throws Exception {
byte[] expectedBytes = ...;
when(exportImportService.exportResource(id, fileType, CodeSystem.class)).thenReturn(ResourceEntity.ok(expectedBytes ));
mockMvc.perform(post("/code-system/export").param("id", "id").param("file-type", "json")).andExpect(content().bytes(expectedBytes ));
verify(exportImportService).exportResource("id", "json", CodeSystem.class);
}
Or if you want to write an integration test you should mock the repo instead of the service:
#MockBean
FhirRepository fhirRepository
#MockBean // or #Mock, #Spy, #SpyBean depends on your fhirRepository implementation.
IParser parser;
#Test
public void yourTest() throws Exception {
byte[] expectedBytes = ...;
Bundle bundle = ...;
when(fhirRepository.resourcesWithCriterion(Mockito.any(), Mockito.any(), Mockito.any()).thenReturn(bundle)
when(fhirRepository.jsonParser()).thenReturn(parser);
when(parser.encodeResourceToString(Mockito.any())).thenReturn(new String(expectedBytes));
mockMvc.perform(post("/code-system/export").param("id", "id").param("file-type", "json")).andExpect(content().bytes(expectedBytes));
}
I am currently writing a JUnit test case for a controller in my application which returns a object (a URL). I am trying to assert the expected and the actual URL to be the same. There are 2 things happening here when I inspect the MvcResult result:
mockResponse has a status code of 200.
In ModelAndView, the model does have the expected url value but when I try to assert the result using result.getResponse().getContentAsString(),
the assertion fails as the result is empty.
What I have already tried:
While debugging, I see the control moving to the service which means that the values were properly mocked and the expected url got returned to the result (as it was present in the ModelAndView when inspected).
I have tried to give the expected url as a json object, used object mapper to read it and then tried a JSONAssert but the result is still empty.
#RunWith(SpringJUnit4ClassRunner.class)
public class StudentControllerTest {
private static final String CACHE_URL= "cacheurl";
#Mock
StudentCacheService studentCacheService;
#InjectMocks
StudentCacheController studentCacheController;
private MockMvc mockMvc;
#Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.standaloneSetup(studentCacheController).build();
}
#Test
public void testGetScoresUrl() throws Exception {
Mockito.when(studentCacheService.getStudentUrl("123", "science"))
.thenReturn(new StudentUrl(CACHE_URL));
MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.get("/student/123/scores")
.header("subject", "science").contentType(MediaType.APPLICATION_JSON)).andExpect(status().is2xxSuccessful())
.andReturn();
Assert.assertEquals(CACHE_URL, result.getResponse().getContentAsString());
}
}
My Controller class is as below:
#Controller
#RequestMapping("/student")
public class StudentCacheController {
#Autowired
StudentCacheService studentCacheService;
#GetMapping(path = "/{studentId}/scores",produces = MediaType.APPLICATION_JSON_VALUE)
public StudentUrl getScores(#PathVariable String studentId, #RequestHeader(value = "subject", required = true) String subject) throws Exception {
return studentCacheService.getStudentUrl(studentId, subject);
}
}
The response is as below:
MockHttpServletResponse:
Status = 200
Error message = null
Forwarded URL = student/123/scores
Included URL = []
ModelAndView:
model = ModelMap
key = studentUrl
value = StudentUrl
url = "cacheurl"
I am receiving this error : org.junit.ComparisonFailure: expected:<[cacheurl]> but was:<[]>
Any help appreciated. Thanks!
I'm trying for more than an hour to test this class. It went so ugly of stubbing the whole components of the method etc. I'd love some advice how to make a better test or refactor the class to make it way easier to test. I could not figure out a way yet.
Class to Test
#Slf4j
public final class HistoryRestService {
static RestTemplate restTemplate = new RestTemplate();
public static Optional<List<History>> findLatestHistories() {
String url = buildUrl();
ResponseEntity<History[]> responseEntity = null;
try {
responseEntity = restTemplate.getForEntity(url, History[].class);
} catch (ResourceAccessException e) {
log.warn("No connection to History persistence. Please check if the history persistence started up properly");
return Optional.empty();
}
History[] histories = responseEntity.getBody();
return Optional.of(Arrays.asList(histories));
}
private static String buildUrl() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("http://");
stringBuilder.append("localhost");
stringBuilder.append(":8081");
stringBuilder.append("/history/get");
return stringBuilder.toString();
}
// For Testing
static void setRestTemplate(RestTemplate restTemplate) {
HistoryRestService.restTemplate = restTemplate;
}
}
Spock Test which fails
class HistoryRestServiceTest extends Specification {
def "test findLatestHistories"() {
given:
History mockedHistory = Mock()
HistoryRestService uut = new HistoryRestService()
History[] expected = [mockedHistory]
RestTemplate mockedRestTemplate = Stub()
ResponseEntity<History> mockedResponseEntity = Stub()
mockedResponseEntity.getBody() >> expected
mockedRestTemplate.getForEntity(_) >> mockedResponseEntity
uut.setRestTemplate(mockedRestTemplate)
when:
def actual = uut.findLatestHistories()
then:
actual.get() == expected
}
}
I'd suggest using real depedency-injection (spring/guice/cdi) instead of static variables.
Furthermore, you should think about what you want to test, is it the correct request and parsing of the network call, then write an integration test using something like mockserver or wiremock to have the whole stack. Or, if you are just concerned with the result handling, then you could move the code that interacts with RestTemplate into a separate method and use partial mocking to mock this method. I'd suggest to use the real integration test, but for the sake of an example this should work, but I didn't verify the code.
#Slf4j
public class HistoryRestService {
private final RestTemplate restTemplate;
public HistoryRestService() {
restTemplate = new RestTemplate();
}
public HistoryRestService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public Optional<List<History>> findLatestHistories() {
try {
return Optional.of(Arrays.asList(getLatestHistories(buildUrl())));
} catch (ResourceAccessException e) {
log.warn("No connection to History persistence. Please check if the history persistence started up properly");
return Optional.empty();
}
}
History[] getLatestHistories(String url) throws {
ResponseEntity<History[]> responseEntity = null;
responseEntity = restTemplate.getForEntity(url, History[].class);
return responseEntity.getBody()
}
private String buildUrl() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("http://");
stringBuilder.append("localhost");
stringBuilder.append(":8081");
stringBuilder.append("/history/get");
return stringBuilder.toString();
}
}
class HistoryRestServiceTest extends Specification {
#Subject
HistoryRestService uut = Spy()
def "test findLatestHistories"() {
given:
History[] expected = [mockedHistory]
when:
def actual = uut.findLatestHistories()
then:
actual.get() == expected
1 * uut.getLatestHistories(_ as String) >> expected
}
def "test findLatestHistories returns empty on exceptions"() {
given:
History[] expected = [mockedHistory]
when:
def actual = uut.findLatestHistories()
then:
!actual.present
1 * uut.getLatestHistories(_ as String) >> {throw new ResourceAccessException()}
}
}
I have the following controller which accept input as #RequestParam
#RequestMapping(value = "/fetchstatus", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Response fetchStatus(
#RequestParam(value = "userId", required = true) Integer userId) {
Response response = new Response();
try {
response.setResponse(service.fetchStatus(userId));
response = (Response) Util.getResponse(
response, ResponseCode.SUCCESS, FETCH_STATUS_SUCCESS,
Message.SUCCESS);
} catch (NullValueException e) {
e.printStackTrace();
response = (Response) Util.getResponse(
response, ResponseCode.FAILED, e.getMessage(), Message.ERROR);
} catch (Exception e) {
e.printStackTrace();
response = (Response) Util.getResponse(
response, ResponseCode.FAILED, e.getMessage(), Message.ERROR);
}
return response;
}
I need a unit test class for this and I am beginner with spring mvc. I don't know writing test classes with #RequestParam as input.
Any help will be appreciated ..
I solved this issue. I just changed the url. Now it contains the parameter as below in test class:
mockMvc.perform(get("/fetchstatus?userId=1").andExpect(status().isOk());
You can use MockMvc for testing Spring controllers.
#Test
public void testControllerWithMockMvc(){
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controllerInstance).build();
mockMvc.perform(get("/fetchstatus").requestAttr("userId", 1))
.andExpect(status().isOk());
}
Also, it is possible to do it using pure JUnit, as long as you need to test only the logic inside your class
#Test
public void testControllerWithPureJUnit(){
Controller controller = new Controller();
//do some mocking if it's needed
Response response = controller.fetchStatus(1);
//asser the reponse from controller
}