Mockito keeps calling the real method and not mock it - java

I'm trying to create a test where I have to mock a method inside the class that I want to test. But it keeps calling the real method, but I want mock it.
The method that I want to mock is
extractSecretValue(String path)
I know it's not mocking the method because there is a "println", and it's printing.
What am I doing wrong?
I'm using JUnit 5
The class that I want to test:
#Configuration
public class RestTemplateConfig {
#Value("${******}")
private String keystore;
#Value("${******}")
private String identificador;
#Value("${******}")
private String token;
#Bean
public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext context = null;
context = SSLContext.getInstance("TLSv1.2");
context.init(null, null, null);
List<Header> headers = new ArrayList<>();
headers.add(new BasicHeader("Authorization", "Bearer " + extractSecretValue(token)));
CloseableHttpClient httpClient = HttpClientBuilder.create().setSSLContext(context).setDefaultHeaders(headers)
.build();
HttpComponentsClientHttpRequestFactory hcchr = new HttpComponentsClientHttpRequestFactory(httpClient);
hcchr.setConnectionRequestTimeout(10000);
return new RestTemplate(hcchr);
}
public String extractSecretValue(String path) {
System.out.println("Test1");
Path secretPath = Paths.get(path);
String value = "";
try (Stream<String> lines = Files.lines(secretPath)) {
value = lines.collect(Collectors.joining());
} catch (IOException ignored) {
throw new ApplicationException(ignored);
}
return value.isEmpty() ? path : value;
}
}
The Test class:
#ExtendWith(MockitoExtension.class)
public class RestTemplateConfigTest {
#Test
public void return_restTemplateConfig() {
RestTemplateConfig restTemplateConfig = new RestTemplateConfig();
RestTemplateConfig restTemplateMock;
RestTemplate restTemplate;
restTemplateMock = Mockito.spy(restTemplateConfig);
try {
when(restTemplateMock.extractSecretValue(anyString())).thenReturn("423424");
restTemplate = restTemplateMock.restTemplate();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new ApplicationException(e);
}
}
}
I've already tried this too:
doReturn("2332").when(restTemplateMock).extractSecretValue(anyString());

If you use when(...).thenReturn(...) the real method will still be invoked
(from Mockito, which is not relevant for your test),
but that should not happen when you use the doReturn(...).when(...) notation instead.
The problem in your test is that token is null and your anyString() does not match that as it only matches non-null strings.
Use any() instead, which matches anything, including nulls.
Combine that with the doReturn(...).when(...) and your test should succeed.

If you do not want the actual methods to be called, then you should be using Mockito.mock() and not Mockito.spy().
you should update your test class to use :
restTemplateMock = Mockito.mock(RestTemplateConfig.class);

You haven't mocked your RestTemplateConfig, you've instantiated it.
What you want is:
restTemplateMock = Mockito.spy(new RestTemplateConfig());

Related

How to do Mockito unit test on method which has a return value ResponseEntity<byte[]>?

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

Mocking LambdaClient not working correctly

I am trying to write a unit test for the below method , where it is invoking the Lambda.
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
public class RoutingService {
private LambdaClient lambdaClient;
private String env;
private String region;
public RoutingService(){
this.env = System.getenv(ENVIRONMENT);
this.region = System.getenv(REGION);
lambdaClient = LambdaClient.builder().build();
}
public RoutingResponse invokeLambda(InvokeLambda request) throws IOException {
RoutingResponse invokeResponse = new RoutingResponse();
String payload = createPayload(request); // Some utility method
String routingURI = getRoutingURI() ; // Another utility method
SDKBytes payloadBytes = SdkBytes.fromUtf8String(payload);
InvokeRequest invokeRequest = InvokeRequest.builder()
.functionName(routingUri)
.payload(payloadBytes)
.invocationType(INVOCATION_TYPE) // INVOCATION_TYPE is caonstant = RequestResponse
.build();
try{
InvokeResponse result;
result = lambdaClient.invoke(invokeRequest); // Was trying to Mock this
String resultString = result.payload().asUtf8String();
invokeResponse = new ObjectMapper().readValue(resultString, RoutingResponse.class);
}catch(AWSServiceExceptuion ex) {
logger.error(new ErrorEvent(ex.awsErrorDetails.errorMessage(), ex.getMessage(), routingUri));
invokeResponse.setStatusCode(ex.statusCode());
invokeResponse.setBody(ex.getMessage());
}
return response;
}
I am trying to write unit test case for this method as below (Mocking the LambdaClient) .
But still it is trying to call the invoke method of Lambda Client.
Means Mocking is not working properly .
public class RoutingServiceTest {
#Mock
private LambdaClient lambdaClient;
#BeforeEach
void setup(){
MockitoAnnotations.initMocks(this);
}
#Test
void invokeLambda() throws IOException, JSONException {
Map<String,String> queryStringParameters = new HashMap<>();
queryStringParameters.put("test","GOOG");
String[] cookies = {"cookiea", "cookieb"};
Map<String,String> headers = new HashMap<>();
headers.put("header1","valuex");
headers.put("header2","valuey");
InvokeLambda fakeRequest = new InvokeLambda.InvokeLambdaBuilder()
.cdnToken("cdn-token").applicationName("eaf-4137-demo")
.service("quote").method("GET")
.path("QuoteService/getQuote")
.queryString(queryStringParameters)
.headers(headers)
.cookies(cookies)
.body("body")
.requestId("request-id")
.build();
InvokeResponse fakeResponse = InvokeResponse.builder().build(); // This class belongs to AWS library
Mockito.when(lambdaClient.invoke(Mockito.any(InvokeRequest.class))).thenReturn(fakeResponse); // This is not working correctly ..
RoutingService routingService = new RoutingService();
RoutingResponse response = routingService.invokeLambda(fakeRequest);
Assertions.assertTrue(response.getStatusCode()==200);
}
}
When I run this test , an Exception is thrown in the try{} block of 'RoutingService' class , because we are passing a dummy "cdn token"
in fakeResponse.
Ideally , it should not invoke the lambda as we are Mocking it.
Please suggest what am I doing wrong here ?
How to correct this ?
You don't have your lambdaClient mock in your instance of RoutingService. In dead your mock is created in your test setup but you create a new instance of RoutingService that will create its own instance of lambdaClient not the mock you created in your test.
I suggest to update your RoutingService to allow constructor dependency injection:
public RoutingService(final LambdaClient lambdaClient){
this.env = System.getenv(ENVIRONMENT);
this.region = System.getenv(REGION);
this.lambdaClient = lambdaClient;
}
And test like:
RoutingService routingService = new RoutingService(lambdaClient);
RoutingResponse response = routingService.invokeLambda(fakeRequest);

mocked function returns null after specifying return

The test code
#Mock
private RestTemplate restTemplate;
#InjectMocks
private ServiceClient client;
#Test
public void getDocument() throws IOException {
String fileExtension = "fileextension";
String host = "docserverurl";
String path = "path";
String content = "content";
client = new ServiceClient(restTemplate, host, fileExtension);
when(restTemplate.getForEntity(any(), any()))
.thenReturn(new ResponseEntity(content, HttpStatus.OK));
assertEquals(content, new String(client.getDocument(path)));
}
and the code under test
public byte[] getDocument(String path) throws IOException {
path = suffixWithExtension(path);
return restTemplate.getForEntity(docServiceHost + DOC_SERVICE_API_VERSION_DEFAULT + DOCUMENT + path, byte[].class).getBody();
}
For some reason, I'm running into an issue where when the getForEntity function is called in the function under test , it returns null instead of the mocked response.
Try this. This should work.
byte[] content = "content".getBytes();
when(restTemplate.getForEntity(anyString(), any()))
.thenReturn(new ResponseEntity(content, HttpStatus.OK));
final byte[] sds = someClass.getDocument("sd");
assertEquals(new String(content), new String(sds));
Some tips. If you are doing this
client = new ServiceClient(restTemplate, host, fileExtension);
you do not need #InjectMocks. It is redundant. It is a best practice to use Constructor Injections and not use Field Injections and #InjectMocks.
I hope you are Mocks are initialized. This is done with
MockitoAnnotations.initMocks(this);
This is also done via some Runner classes(If you are using any)

Mockito "thenThrow" doesn't throw the exception when expected

I have an issue when trying to test a class that represents a Rest Client. I'm using RestTemplate in Spring Boot.
This is the abstract RestClient class:
public abstract class RestClient {
...
public RestResponse sendPostRequest(URI baseUri, String resource, IRestRequest restRequest, ClassresponseClass)
throws ServerException, ClientException {
...
try {
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(baseUri, HttpMethod.POST, getEntity(restRequest), responseClass);
result = response.getBody();
getLogger().debug("[{}] received", result);
return result;
} catch (HttpClientErrorException e) {
throw new ClientException(e.getCause());
} catch (HttpServerErrorException e) {
throw new ServerException(e.getCause());
} catch (Exception e) {
getLogger().error("Error with cause: {}.", e.getMessage());
}
...
}
}
This is the actual implementation:
public class ActualRestClient extends RestClient {
public RestResponse sendFetchFileRequest(URI baseUri, FetchFileRequest request) throws ServerException, ClientException {
return sendPostRequest(baseUri, "FETCH_FILE", request, RestResponse.class);
}
}
An this is the test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ActualRestClient.class, RestClient.class})
public class ActualResRestClientTest {
private static final String REQUEST_URI = "something";
#InjectMocks
public ActualRestClient testee;
#Mock
private RestTemplate restTemplate;
#Test(expected = ServerException.class)
public void sendPostRequestWithResponseBody_throwsServerException() throws Exception {
HttpServerErrorException httpServerErrorException = new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR);
when(restTemplate.exchange(Mockito.any(URI.class), eq(HttpMethod.POST), Mockito.any(), eq(FetchFileRequest.class))).thenThrow(httpServerErrorException);
testee.sendFetchFileRequest(new URI(REQUEST_URI), new FetchFileRequest());
}
}
ClientException and ServerException are exceptions created by me by extending Exception class.
My problem is that in the RestClient class another Exception is catched(message:"URI is not absolute") instead of HttpServerErrorException and I can't understand why. Thank you!
As the commenter already expressed: doing new URI("something") already throws at you. But even when you pass a "valid" URI, your code will not work, as there is a misconception on your end. You see:
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(baseUri, HttpMethod.POST, getEntity(restRequest), responseClass);
That code lives within a method of your class under test. But #InjectMocks works only for fields of classes.
In other words: when your production code gets executed, a new (completely different** ResponseTemplate instance is created. And therefore your mocking spec is irrelevant, because the method isn't invoked on your mock in the first place.
Two choices:
turn that local variable into a field of your class under test (then injecting should work)
or, as you are already using PowerMock(ito), you could use that mocking framework to intercept that call to new().
I suggest you rather use option one, and avoid to use the PowerMock(ito) extension altogether!

Can't manage to test rest template helper class

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

Categories

Resources