Hot questions for Using Mockito in resttemplate
Question:
public class ServiceTest { @Mock RestTemplate restTemplate = new RestTemplate(); @InjectMocks Service service = new Service(); ResponseEntity responseEntity = mock(ResponseEntity.class); @Test public void test() throws Exception { Mockito.when(restTemplate.getForEntity( Mockito.anyString(), Matchers.any(Class.class) )) .thenReturn(responseEntity); boolean res = service.isEnabled("something"); Assert.assertEquals(res, false); }
I tried to test a simple test for a service including a restclient. It looks I have not Mock the RestTemplate
successfully. It looks like the code get the real data not the mock one. Anyone can help me with this.
The service itself will looks as this:
public class Service{ public boolean isEnabled(String xxx) { RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.getForEntity("someurl",String.class); if(...)return true; return false; } }
Answer:
The problem is that in your isEnabled
you are creating a new RestTemplate. This is wrong for two reasons, one is that you cannot mock it since you are creating a new one, and second it is good to avoid creating new objects per request. RestTemplate is thread safe and hence can be a service class member, being used across many threads.
Change your service class to something like this:
public class Service{ RestTemplate restTemplate = new RestTemplate(); public boolean isEnabled(String xxx) { ResponseEntity<String> response = restTemplate.getForEntity("someurl",String.class); if(...)return true; return false; } }
Now that your RestTemplate has become a class member you can now properly mock through one of two ways. One, inject it using the @InjectMock
, or use a setter method that you call from your test.
Since you are using InjectMock in your code we can go with that.
@RunWith(MockitoJUnitRunner.class) public class ServiceTest { @Mock RestTemplate restTemplate; @InjectMocks @Spy Service service; ResponseEntity responseEntity = mock(ResponseEntity.class); @Test public void test() throws Exception { Mockito.when(restTemplate.getForEntity( Mockito.anyString(), ArgumentMatchers.any(Class.class) )) .thenReturn(responseEntity); boolean res = service.isEnabled("something"); Assert.assertEquals(res, false); }
Notice that I made a few changes. First, I removed the new RestTemplate()
and new Service()
. You should let mockito create those for you. By annotating them with @Mock
and @Spy
you will ensure that Mockito will create them for you, and more importantly, will inject the mocks into your service
object.
Question:
I have a service class, written in spring, with some methods. One of this acts as a restful consumer like below:
..... HttpEntity request = new HttpEntity<>(getHeadersForRequest()); RestTemplate restTemplate = new RestTemplate(); String url = ENDPOINT_URL.concat(ENDPOINT_API1); UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url) .queryParam("param1", parameter1); ReportModel infoModel = null; try{ infoModel = restTemplate.exchange(builder.toUriString(), HttpMethod.GET, request, ReportModel.class).getBody(); }catch (HttpClientErrorException | HttpServerErrorException e){ e.printStackTrace(); }
I want to use Mockito
to mock my service, but every method that interacts with restful server instance a new RestTemplate. I've to create a static class to Inject it into my service?
Answer:
One of the benefits from dependency injection is to be able to easily mock your dependencies. In your case it would be a lot easier to create a RestTemplate
bean:
@Bean public RestTemplate restTemplate() { return new RestTemplate(); }
And in stead of using new RestTemplate()
in your client you should use:
@Autowired private RestTemplate restTemplate;
For the unit testing with Mockito you'll have to mock the RestTemplate
, for example by using:
@RunWith(MockitoJUnitRunner.class) public class ClientTest { @InjectMocks private Client client; @Mock private RestTemplate restTemplate; }
In this case Mockito will mock and inject the RestTemplate
bean in your Client
. If you don't like mocking and injecting through reflection you can always go for a separate constructor or setter to inject the RestTemplate
mock.
Now you can write a test like this:
client.doStuff(); verify(restTemplate).exchange(anyString(), eq(HttpMethod.GET), any(HttpModel.class), eq(ReportModel.class));
You'll probably want to test more than that, but it gives you a basic idea.
Question:
My service class code is as below:
public class MyServiceImpl implements MegatillAccessService { @Autowired RestTemplate restTemplate; @Value("${api.key}") private String apiKey; @Value("${customers.url}") private String postUrl; @Override public String pushCustomerData(List<Customer> listOfcustomers, String storeId) throws MyServiceException { Set<Customer> setOfCustomers = new HashSet<>(listOfcustomers); int noOfCustomersLoadedSuccessfully =0; MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>(); headers.add("apiKey", apiKey); headers.add("Content-Type", "application/json"); headers.add("storeId", storeId); restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); for(Customer customer: setOfCustomers){ HttpEntity<Customer> request = new HttpEntity<Customer>(customer, headers); CustomerDataDto customerDataDto = null; try { customerDataDto = restTemplate.exchange(postUrl, HttpMethod.POST, request, CustomerDataDto.class).getBody(); } catch (HttpClientErrorException ex) { if (ex.getStatusCode().equals(HttpStatus.NOT_FOUND)) { log.error("The customers service is not available to load data: "+ ex.getResponseBodyAsString(), ex); throw new MyServiceException("The customers service is not available to load data",new RuntimeException(ex)); } else{ log.warn("Error for customer with alias: "+customer.getAlias() +" with message: "+ ex.getResponseBodyAsString(), ex); if(!ex.getResponseBodyAsString().contains("already found for this shop")){ throw new MyServiceException("An error occurred while calling the customers service with status code "+ex.getStatusCode(),new RuntimeException(ex)); } } } catch(Exception e){ throw new MyServiceException("An error occurred while calling the customers service: ",new RuntimeException(e)); } if(null != customerDataDto) { noOfCustomersLoadedSuccessfully++; log.debug("--------Data posted successfully for: ---------"+customerDataDto.getAlias()); } } String messageToReturn = "No. of unique customers from source: "+setOfCustomers.size()+". No. of customers loaded to destination without error: "+noOfCustomersLoadedSuccessfully; return messageToReturn; } }
My Test class is as below:
@RunWith(MockitoJUnitRunner.class) @SpringBootTest public class MyServiceTest { @InjectMocks private MyService myService = new MyServiceImpl(); @Mock RestTemplate restTemplate; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); initliaizeModel(); restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); } @Test public void pushAllRecords(){ Mockito.when(restTemplate.exchange(Matchers.anyString(), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto); /*Mockito.when(restTemplate.exchange(Mockito.anyString(), Mockito.<HttpMethod> eq(HttpMethod.POST), Matchers.<HttpEntity<?>> any(), Mockito.<Class<CustomerDataDto>> any()).getBody()).thenReturn(customerDataDto);*/ String resultReturned = myService.pushCustomerData(customers,"1235"); assertEquals(resultReturned, "No. of unique customers from source: 2. No. of customers loaded to destination without error: 2"); } }
While running the test, I am getting NullPointerException at the line where I am giving the Mockito.when and thenReturn condition. I tried many combinations but it is still giving NPE. I can't even reach the method invocation.Can you please let me know where am I going wrong?
Answer:
You get NullPointerException
because you are doing too much in your Mockito.when
. Your code inside when
(shorter version):
restTemplate.exchange(args).getBody()
You are trying to mock getBody()
but it is called on exchange(args)
. And what does exchange(args)
returns? Mockito doesn't know what it should return and you didn't specify that so by default it returns null
.
That's why you get NPE.
To fix this you can either do mocking step by step, ie.
ResponseEntity re = Mockito.when(exchange.getBody()).thenReturn(customerDataDto); Mockito.when(restTemplate.exchange()).thenReturn(re);
or specify mock to return deep stubs, like that (if you want to use annotations):
@Mock(answer = Answers.RETURNS_DEEP_STUBS) RestTemplate restTemplate;
Question:
Can't figure out the correct way to use matchers to identify which overload of the exchange method I am targetting. The call I am making:
restTemplate.exchange(url, HttpMethod.PUT, httpEntity, Object.class)
I've tried using any(Class.class), and a couple other things but nothing is working. There are 2 methods with a similar signature that I am trying to distinguish between:
exchange(String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType)
and
exchange(String var1, HttpMethod var2, @Nullable HttpEntity<?> var3, ParameterizedTypeReference<T> var4)
Here are my current imports related to Mockito:
import org.mockito.Mock; import org.mockito.MockitoAnnotations; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*;
Has anyone been able to mock a call to this method that uses a Class as the 4th parameter instead of a ParameterizedTypeReference?
Answer:
I am not sure whether I misunderstood your question or the issue mentioned by @MarciejKowalski
, but when running the test from the issue or what I suppose is similar to your example against mockito-core-2.23.4
/ JDK 1.8.0_151
it works just fine.
[I used JUnit 4 for your example instead of JUnit 5]
import static org.mockito.ArgumentMatchers.any; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; @RunWith(MockitoJUnitRunner.class) public class MockitoTest { @Test public void test() { RestTemplate api = Mockito.mock(RestTemplate.class); ResponseEntity<?> response1 = Mockito.mock(ResponseEntity.class); ResponseEntity<?> response2 = Mockito.mock(ResponseEntity.class); Mockito.when(api.exchange(any(String.class), any(HttpMethod.class), any(HttpEntity.class), any(Class.class))).thenReturn(response1); Mockito.when(api.exchange(any(String.class), any(HttpMethod.class), any(HttpEntity.class), any(ParameterizedTypeReference.class))).thenReturn(response2); ParameterizedTypeReference mock = Mockito.mock(ParameterizedTypeReference.class); Assert.assertEquals(response1, api.exchange("", HttpMethod.GET, new HttpEntity(""), String.class)); Assert.assertEquals(response2, api.exchange("", HttpMethod.GET, new HttpEntity(""), mock)); } }
Question:
I am trying to mock this service that calls an API using rest template and returns a List.
I am unable to mock the restTemplate.exchange()
method. It's giving me a "java.lang.IllegalArgumentException: URI is not absolute" exception.
edit- My stupid self forgot to put http:// before the base url in the test case and that is why I was getting that. Thanks for helping and apologies for wasting your time.
Method to be tested
@Value("${base-url}") private String baseUrl; @Override public List<Currency> getCurrencyList() { RestTemplate restTemplate = new RestTemplate(); String url = baseUrl + "/currency"; ResponseEntity<List<Currency>> result; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> dataHttpEntity = new HttpEntity<>(headers); log.info(url); result = restTemplate.exchange(url, HttpMethod.GET,dataHttpEntity, new ParameterizedTypeReference<List<Currency>>() { }); return result.getBody(); }
The Testing code
@Test public void getCurrencyListTest() { ResponseEntity<List<Currency>> result = ResponseEntity.ok(currencyList); when(restTemplate.exchange( ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.any(), ArgumentMatchers.<Class<List<Currency>>>any())).thenReturn(result); assertEquals(currencyList,service.getCurrencyList()); }
Exception
java.lang.IllegalArgumentException: URI is not absolute at java.net.URI.toURL(URI.java:1088) at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145) at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:87) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:721) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:682) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:627) at com.fpts.seller.service.external.impl.DocumentServiceImpl.getCurrencyList(DocumentServiceImpl.java:34) at com.fpts.seller.service.external.DocumentServiceImplTest.getCurrencyListTest(DocumentServiceImplTest.java:105) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:78) at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:84) at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39) at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Answer:
Give a protocol name for your baseUrl and try again please, i mean by protocol is : http or https .
For example:
String url = "http://" + baseUrl + "/currency";
Question:
I have a method:
public void putObj(Doc doc) { for (int i = 0; i < 3; i++) { try { OAuth2RestTemplate restTemplate = something.thatReturnsOAuth2RestTemplate(props); restTemplate.postForEntity(somethingElse.getUrl(), doc.toJSONString(), String.class); break; } catch (HttpClientErrorException | HttpServerErrorException e) { //do stuff in here } } }
And my test class:
@RunWith(MockitoJUnitRunner.class) @PrepareForTest(OkHttp3TemplateUtil.class) public class TestingClass { @InjectMocks private static MyService myService; @Mock private static Something something; @Mock private static Props props; @Mock private static OAuth2RestTemplate restTemplate; @Test public void testExceptionCaughtWhenThrownByRestTemplate(){ PowerMockito.mockStatic(OkHttp3TemplateUtil.class); Doc doc = new Doc.docBuilder().setSomething(""); when(something.thatReturnsOAuth2RestTemplate(props)).thenReturn(restTemplate); when(restTemplate.postForEntity("http://dummy.com", String.class, String.class)).thenThrow(HttpClientErrorException.class); myService.putObj(doc); } }
No matter what I do, thenThrow
never throws an exception. The test passes never providing coverage for code after catch
. What am I missing here, I'm going mad!
Answer:
It looks like you need to use matchers from Mockito.
In your case the 3 args for restTemplate
are a bit confusing. The 1st is a String
value so use anyString()
to match it and mock out somethingElse.getUrl()
, that code isn't in the example so not sure what it does but it must return a String
and not be null
. It looks like you want to match any string for the 2nd, with Mockito you need to use anyString()
or any()
if it is not a String
to accomplish that. The 3rd is the actual value of String.class
so again use eq()
. Note, if any params are null it will not match. Also, it is easy to end up mocking out a different overloaded postForEntity
if you're not careful.
For the something.thatReturnsOAuth2RestTemplate
, you might be OK without a matcher. If the Props
class has equals defined and both the test and production code values are equal. However, the example doesn't show this info, so I just added the any(Props.class)
for that too.
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @Test public void testExceptionCaughtWhenThrownByRestTemplate(){ PowerMockito.mockStatic(OkHttp3TemplateUtil.class); Doc doc = new Doc.docBuilder().setSomething(""); when(something.thatReturnsOAuth2RestTemplate(any(Props.class))).thenReturn(restTemplate); when(restTemplate.postForEntity(anyString(), any(), eq(String.class))).thenReturn(response); }
Question:
I am currently writing unit test for below method
@Autowired private RequestConfig requestConfig; @Autowired private RetryTemplate retryTemplate; public ResponseEntity<String> makeGetServiceCall(String serviceUrl) throws Exception { try { return retryTemplate.execute(retryContext -> { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = requestConfig.createHttpHeaders(); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.GET, entity, String.class); return response; }); } catch (Exception e) { throw new Exception("Generic exception while makeGetServiceCall due to" + e + serviceUrl); } }
UPDATED METHOD:
@Autowired private RequestConfig requestConfig; @Autowired private RetryTemplate retryTemplate; @Autowired private RestTemplate restTemplate; public ResponseEntity<String> makeGetServiceCall(String serviceUrl) throws Exception { try { return retryTemplate.execute(retryContext -> { HttpHeaders headers = requestConfig.createHttpHeaders(); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); ResponseEntity<String> response = restTemplate.exchange(serviceUrl, HttpMethod.GET, entity, String.class); return response; }); } catch (Exception e) { throw new Exception("Generic exception while makeGetServiceCall due to" + e + serviceUrl); } }
I tried all possibilities but I am unable to get it right. Here is my below test.
@Mock private RestTemplate restTemplate; @Mock public RequestConfig requestConfig; @InjectMocks private RetryTemplate retryTemplate; ServiceRequest serviceRequest; @Test public void makeGetServiceCall() throws Exception { String url = "http://localhost:8080"; RetryTemplate mockRetryTemplate = Mockito.mock(RetryTemplate.class); RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class); ResponseEntity<String> myEntity = new ResponseEntity<>(HttpStatus.ACCEPTED); Mockito.when(mockRetryTemplate.execute(ArgumentMatchers.any(RetryCallback.class), ArgumentMatchers.any(RecoveryCallback.class), ArgumentMatchers.any(RetryState.class))).thenReturn(myEntity); Mockito.when(mockRestTemplate.exchange( ArgumentMatchers.eq(url), ArgumentMatchers.eq(HttpMethod.GET), ArgumentMatchers.<HttpEntity<String>>any(), ArgumentMatchers.<Class<String>>any()) ).thenReturn(myEntity); ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url); Assert.assertEquals(myEntity, response); }
UPDATED TEST CASE:
@Mock public RequestConfig requestConfig; @Mock private RestTemplate restTemplate; @Mock private RetryTemplate retryTemplate; @InjectMocks ServiceRequest serviceRequest; @Test public void makeGetServiceCall() throws Exception { //given: String url = "http://localhost:8080"; when(requestConfig.createHttpHeaders()).thenReturn(null); ResponseEntity<String> myEntity = new ResponseEntity<>( HttpStatus.ACCEPTED); when(retryTemplate.execute(any(RetryCallback.class), any(RecoveryCallback.class), any(RetryState.class))).thenAnswer(invocation -> { RetryCallback retry = invocation.getArgument(0); return retry.doWithRetry(/*here goes RetryContext but it's ignored in ServiceRequest*/null); }); when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(String.class))) .thenReturn(myEntity); //when: ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url); //then: assertEquals(myEntity, response); }
The response object which I get from my method call makeGetServiceCall
always return null. When I debug the code I see exception org.mockito.exceptions.misusing.WrongTypeOfReturnValue: ResponseEntity cannot be returned by toString() toString() should return String
error on the resttemplate
mocking where I return myEntity
I am not sure what am I missing.
Answer:
Well, you have made quite some number of mistakes...
- I'm sure you wanted to annotate
private RetryTemplate retryTemplate;
with@Mock
, not@InjectMocks
@InjectMocks
should go ontoServiceRequest serviceRequest;
- You are defining interactions on some
mockRetryTemplate
andmockRestTemplate
which have nothing to do withserviceRequest
. Instead, you should use your@Mock
-annotated fields to define interactions on because they are being injected into your object under test (serviceRequest
) - Moreover, you can't normally mock
RestTemplate
and inject it into yourServiceRequest
because you don't use dependency injection in the first place forRestTemplate
inServiceRequest
. You just instantiate its instance inServiceRequest.makeGetServiceCall
- You are defining an interaction on the wrong method at line
Mockito.when(retryTemplate.execute(...
. Your interaction specifiesRetryTemplate.execute(RetryCallback, RecoveryCallback, RetryState)
whereas yourServiceRequest
uses another methodRetryTemplate.execute(RetryCallback)
- You should also notice that
RetryTemplate.execute
is final and so you can't mock it without extra efforts as explained here. And generally, you should prefer interfaces over classes, e.g.RestOperations
andRetryOperations
overRestTemplate
andRetryTemplate
respectively, to be more flexible.
That said, below is the working test which solves your problem. But take note of removing RestTemplate restTemplate = new RestTemplate();
from ServiceRequest
and making restTemplate
a field so it's dependency-injected.
@RunWith(MockitoJUnitRunner.class) public class ServiceRequestTest { @Mock private RestTemplate restTemplate; @Mock public RequestConfig requestConfig; @Mock private RetryTemplate retryTemplate; @InjectMocks ServiceRequest serviceRequest; @Test public void makeGetServiceCall() throws Exception { //given: String url = "http://localhost:8080"; ResponseEntity<String> myEntity = new ResponseEntity<>(HttpStatus.ACCEPTED); when(retryTemplate.execute(any(RetryCallback.class))).thenAnswer(invocation -> { RetryCallback retry = invocation.getArgument(0); return retry.doWithRetry(/*here goes RetryContext but it's ignored in ServiceRequest*/null); }); when(restTemplate.exchange(eq(url), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class))) .thenReturn(myEntity); //when: ResponseEntity<String> response = serviceRequest.makeGetServiceCall(url); //then: assertEquals(myEntity, response); } }
Question:
I'm trying to mock a rest template in my DAO class but Mockito throws weird error saying it isn't able to mock.
Trying to cover unit test cases for my Spring boot app version 2.x. I have almost tried all possible solutions over the internet like updating JDK/JRE for compilation but I'm stuck with the following error:
org.mockito.exceptions.base.MockitoException: Mockito cannot mock this class: class org.springframework.web.client.RestTemplate. Mockito can only mock non-private & non-final classes. If you're not sure why you're getting this error, please report to the mailing list. Java : 1.8 JVM vendor name : Oracle Corporation JVM vendor version : 25.181-b13 JVM name : Java HotSpot(TM) 64-Bit Server VM JVM version : 1.8.0_181-b13 JVM info : mixed mode OS name : Windows 10 OS version : 10.0 Underlying exception : java.lang.IllegalArgumentException: Could not create type at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:115) ....
Following is my code:
build.gradle
dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-data-rest' implementation 'org.springframework.retry:spring-retry' implementation 'org.aspectj:aspectjrt' implementation 'org.aspectj:aspectjweaver' implementation 'org.springframework:spring-aop' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0' testCompile 'org.junit.jupiter:junit-jupiter-params:5.2.0' testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.2.0' testImplementation 'org.mockito:mockito-core:2.+' testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3' } test { testLogging.showStandardStreams = true useJUnitPlatform() }
MyDao.java
@Repository public class MyDao { @Value("${app.prop.service.url}") private String url; @Autowired public RestTemplate restTemplate; public String getSignals() { System.out.println("url -----------------------> " + url); return new RetryTemplate().execute(context -> { ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); if (response.getStatusCodeValue() == 200) { System.out.println("Server response -> " + response.getBody()); return response.getBody(); } else { throw new RuntimeException("server response status: " + response.getStatusCode()); } }, context -> { System.out.println("retry count: " + context.getRetryCount()); System.err.println("error -> " + context.getLastThrowable()); return null; }); } }
MyDaoTest.java
@ExtendWith(MockitoExtension.class) public class MyDaoTest { @Mock private RestTemplate restTemplate; @InjectMocks private MyDao dao; @BeforeEach public void prepare() { ResponseEntity<String> response = new ResponseEntity<>("{name: myname}", HttpStatus.OK); Mockito.doReturn(response).when(restTemplate.getForEntity(Mockito.anyString(), String.class)); } @Test public void testGetSignals() { System.out.println("------------------TEST-------------------"); String result = dao.getSignals(); System.out.println("result ------>" + result); assertEquals("{name: myname}", result); } }
BeanConfig for RestTemplate
@Bean public RestTemplate restTemplate() { // block of code for SSL config for HTTPS connection return new RestTemplate(); }
Any suggestions will be really helpful
P.S: The application is running perfectly fine through gradle command
gradlew bootRun
The problem is only with unit testing
gradlew test
Answer:
One cause of the described (or a subordinate) problem might be, since RestTemplate
is nor "private" nor "final" and "known as mockable", the invocation/mocking of restTemplate.getForEntity()
... in the current version, this method is available in three flavors/with overloading parameters:
... getForEntity(String url, Class<T> responseType, Object... uriVariables) ...
... getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) ...
... getForEntity(URI url, Class<T> responseType) ...
In your (operative) code you seem to use the 1st flavor, so without changing it (the operative code), I propose to adjust your test code to:
...restTemplate.getForEntity(Mockito.anyString(), String.class /*!!!*/, ArgumentMatchers.<Object>any());
See also:
- Mockito not working for RestTemplate
- How to properly match varargs in Mockito
But the error message still makes me "cautious" & wondering... you are on the "beeding edge" junit (5) stack, are you confident regarding your test setup!? (missing gradle libs/config?)
Please, "try":
testCompile "org.mockito:mockito-core:2.+" testCompile('org.mockito:mockito-junit-jupiter:2.18.3')
not:
... testImplementation 'org.mockito:mockito-core:2.+' testImplementation 'org.mockito:mockito-junit-jupiter:2.18.3'
..as shown at dzone's spring-boot-2-with-junit-5-and-mockito-2-for-unit. If that (and other findings from the tutorial) doesn't help, consider to really:
report (it) to the mailing list(.)
And i heard/read the first time (in my life):
mock a rest template in my DAO class
Consider to name your dao as "service" (+ the according steps/refactorings;)
Question:
I have the following code that is inside of a method that I am testing. I need to mock this restTemplate
call to get predictable result.
GitHubEmail[] gitHubEmails = restTemplate .getForObject(userEmailsUrl, GitHubEmail[].class, oAuthToken);
In the test method, I do this:
RestTemplate mockRestTemplate = Mockito.mock(RestTemplate.class); GitHubEmail fakeGitHubEmail = new GitHubEmail("testemail@email.com", false, false, GitHubEmailVisibility.PRIVATE); GitHubEmail[] fakeEmails = {fakeGitHubEmail}; Mockito.when(mockRestTemplate.getForObject( Mockito.eq(userUrl), Mockito.eq(GitHubEmail[].class), Mockito.eq(testOAuthToken))) .thenReturn(fakeEmails); gitHubService.setRestTemplate(mockRestTemplate); User user = gitHubService.getUser(testOAuthToken);
Things aren't working as I expect them to... When I examine gitHubEmails
variable in my method I am testing, it's null
.
Why isn't this working?
Answer:
The current code as it is right now does not contain any mistakes. However, there are two things we don't see from the given code:
- We don't see that
testOAuthToken
is properly passed to theoAuthToken
variable within thegithubService
. - We don't see that the
userUrl
is passed to theuserEmailsUrl
withingithubService
.
You should make sure that all properties match the one you expect them to be, otherwise the mocking doesn't work. Given that you named one property userUrl
and the other one userEmailsUrl
, it's likely that the error is there.
Usually, when I encounter these error-prone mocking situations, I use "any matchers" (any()
, anyString()
, ...) when mocking and then after the call and the assertions, I use Mockito.verify()
to check if the parameters match:
Mockito.when(mockRestTemplate.getForObject( Mockito.anyString(), // Use anyString() Mockito.eq(GitHubEmail[].class), Mockito.anyString())) // Use anyString() .thenReturn(fakeEmails); // Call + Assertions ... Mockito.verify(mockRestTemplate).getForObject( Mockito.eq(userUrl), // Use eq() Mockito.eq(GitHubEmail[].class), Mockito.eq(testOAuthToken)); // Use eq()
The reason for this is that the verify()
output gives a lot more feedback. Rather than just failing, it will tell why it failed when:
- The mocked method was called with different arguments, and which arguments
- The mocked object had different methods being invoked
Question:
I have a service method with restTemplate. As part of unit test, I am trying to mock it but some how failing.
Service Method:
@Autowired private RestTemplate getRestTemplate; return getRestTemplate.getForObject(restDiagnosisGetUrl, SfdcCustomerResponseType.class);
Test Method:
private CaresToSfdcResponseConverter caresToSfdcResponseConverter; @Before public void setUp() throws Exception { caresToSfdcResponseConverter = new CaresToSfdcResponseConverter(); } @Test public void testConvert(){ RestTemplate mock = Mockito.mock(RestTemplate.class); Mockito.when(mock.getForObject(Matchers.anyString(), Matchers.eq(SfdcCustomerResponseType.class))).thenReturn(sfdcCustomerResponseType); } sfdcRequest = caresToSfdcResponseConverter.convert(responseForSfdcAndHybris);
It is giving NullPointerException. Looks like it is failing to mock rest template and it is breaking there as rest template is null. Any help would appreciated.Thanks
Answer:
It's not failing to mock the rest template, but it's not injecting the mocked rest template to your production class. There are at least two ways to fix this.
You can change your production code and use constructor injection. Move the RestTemplate to the constructor as a parameter and then you can just pass the mock in the test:
@Service public class MyService { @Autowired public MyService(RestTemplate restTemplate) { this.restTemplate = restTemplate; } }
In your test you will simply create the service as any other object and pass it your mocked rest template.
Or you can change your test to inject your service using the following annotation:
@RunWith(MockitoJUnitRunner.class) public class MyServiceTest { @InjectMocks private MyService myService; @Mock private RestTemplate restTemplate; @Test public void testConvert(){ Mockito.when(mock.getForObject(Matchers.anyString(), Matchers.eq(SfdcCustomerResponseType.class))).thenReturn(sfdcCustomerResponseType); } }
You can see an example in another SO question: Using @Mock and @InjectMocks
I generally prefer constructor injection.
Question:
I am trying to unit test the following Spring boot code with RestTemplate sending a request to the API and retrieving some resources from it:
final URI targetUri = UriComponentsBuilder.fromUriString(baseUri) .path("/myEntities").build().toUri(); final RequestEntity<Void> request = RequestEntity.get(targetUri).accept(HAL_JSON).build(); final Resources<MyEntity> resourceAccounts = restTemplate.exchange(request, new ResourcesType<MyEntity>() { }).getBody();
In the unit test, I am mocking this request-response using Mockito:
final Resources<MyEntity> myEntities = new Resources<>(myEntityList, links); final ResponseEntity<Object> response = new ResponseEntity<Object>(myEntities, HttpStatus.OK); when(restTemplate.exchange(any(RequestEntity.class), any(ResourcesType.class))).thenReturn(response);
It works fine but I am getting Unchecked invocation exchanged
because I am not using the generics correctly.
I just wonder what is the correct and bullet-proof way of doing this? I tried casting the ResponseEntity
type to MyEntity
but this does causes compile exception (the constructor ResponseEntity<MyEntity> is undefined
).
Answer:
With type safe method of Matchers
? One of the way to check method invocations with the generics.
Matchers.<ResponseType<MyEntity>>.any()
Source - Reference
To check the response of a method, ArgumentCaptor
can be used.
Question:
I am trying to mock a call to RestTemplate.exchange() but cant get it to work. Currently the call to exchange() hangs so I believe the actual method is being called instead of my mock. The call to exchange() is as follows:
ResponseEntity<List<MyType>> response = restTemplate.exchange(queryStr, HttpMethod.GET, null, new ParameterizedTypeReference<List<MyType>>() { });
The mocking is as follows:
@MockBean private RestTemplate restTemplate; @Test public void testMethod() throws Exception { when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), eq(null), eq(new ParameterizedTypeReference<List<MyType>>(){}) )).thenReturn(new ResponseEntity<List<MyType>>(HttpStatus.OK)); // rest of test code follows. }
I have tried changing the argument matchers around so they match a broader argument types (ie. any() in place of anyString()) but I get the same behavior or an error "reference to exchange is ambiguous both method exchange(...) and method exchange(...) match". I also get "no suitable method found for thenReturn(...) is not compatible with thenReturn(...)" along with the first error.
Thanks in advance.
Answer:
Found that we did not annotate the instance of the RestTemplate with @Autowired that was used in our controler.
@RestController public class myController { ... @Autowired // <-- Forgot this annotation. private RestTemplate restTemplate; ... }
Now mocks work correctly.
Question:
I have a generic method which invokes specified URL using RestTemplate.exchange
. Method itself is working and loading data fine but I am not able to unit test it using Mockito.
Main Method
@Service public class MyClass{ private <T> List<T> loadData(String url) { return restTemplate.exchange( url, GET, null, new ParameterizedTypeReference<List<T>>(){} ).getBody().stream().collect(toList())); } }
Unit Test
@Runwith(MockitoJUnitRunner.class) public class MyTest { @Mock private RestTemplate restTemplate; @Test public void givenCall_myMethod_WillReturnData(){ given(restTemplate.exchange( ArgumentMatchers.anyString(), ArgumentMatchers.any(), any(), any(Class.class) )) .willReturn(bodyData()); } }
If I use non-generic version then everything works fine, however mockito returns NullPointerException
with generics version.
What's wrong or missing?
Answer:
The last wildcard you have defined as: any(Class.class)
.
The exchange method has signature:
exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) throws RestClientException
You should define it as: any(ParameterizedTypeReference.class)
Also I would suggest replacing the very vague any()
set-us with a any(Class)
equivalents.
Question:
I am trying to mock a Rest Client using Mockito which takes in the following:-
MainServiceClass.java
public String serviceCall(String s1){ String data2 = "s1"; try{ headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("headers1", "headers1"); HttpEntity<String> entity = new HttpEntity<String>(data2, headers); ResponseEntity<Object> responseEntity = restTemplate.exchange(someurl, HttpMethod.POST, entity, Object.class); return someString; }
ServiceTest.java
@RunWith(MockitoJUnitRunner.class) @SpringBootTest public class RestTemplateTests { @InjectMocks @Spy private Someservice service; @Autowired private MockMvc mockMvc; @Mock RestTemplate restTemplate; ResponseEntity responseEntity = mock(ResponseEntity.class); @Test public void myCallToTemplate() throws Exception { @Test public Mytestforcallingservice() { Mockito.when(restTemplate.exchange( someurl, HttpMethod.POST, Mockito.<HttpEntity<String>> any(), Mockito.<Class<?>> any())).thenReturn(responseEntity); String s2 = service.serviceCall(s1); Assert.assertEquals(s2, "hey"); }
Below is my Error:-
.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers! 4 matchers expected, 2 recorded: -> at com.example.MyServiceClass.Mytestforcallingservice(ServiceTest.java:110) -> at com.example.ServiceTest.Mytestforcallingservice(ServiceTest.java:110) This exception may occur if matchers are combined with raw values: //incorrect: someMethod(anyObject(), "raw String"); When using matchers, all arguments have to be provided by matchers. For example: //correct: someMethod(anyObject(), eq("String by matcher"));
The Test fails at forming HttpEntity which has both data2 and headers. How can I form that in the Mockito call ?
Also, how can I verify it.
Thank you.
Answer:
Mockito is saying you can't use argument matchers (in your case Mockito.any()) with raw values (in your case "someurl" and HttpMethod.POST) So either you remove your argument matchers or you add an argument matcher to your raw values. Mockito.eq on "someurl" and HttpMethod.POST in your case would trigger the mock in your test. see below.
Also if you want to verify your rest template was invoked in a particular way you can use Mockito.verify(). See below to verify the rest template was invoked with a particular url
import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; public class MyService { private String someurl = "myurl"; private RestTemplate restTemplate = new RestTemplate(); public String serviceCall(String s1) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.set("headers1", "headers1"); HttpEntity<String> entity = new HttpEntity<String>(s1, headers); ResponseEntity<String> responseEntity = restTemplate.exchange(someurl, HttpMethod.POST, entity, String.class); return responseEntity.getBody(); } }
and
import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.client.RestTemplate; @RunWith(MockitoJUnitRunner.class) public class RestTemplateTests { @Mock private RestTemplate restTemplate; private String testUrl = "myurl"; @InjectMocks private MyService serviceClass; @Test public void test1() { String expectedBody = "hey"; ResponseEntity<String> responseEntity = new ResponseEntity<>(expectedBody, HttpStatus.OK); when(restTemplate.exchange(eq(testUrl), eq(HttpMethod.POST), any(), eq(String.class))) .thenReturn(responseEntity); String s2 = serviceClass.serviceCall("s1"); verify(restTemplate, times(1)).exchange(eq(testUrl), any(), any(), eq(String.class)); assertEquals(s2, expectedBody); } }