Mock UriInfo is not working - java

I'm trying to setup a couple of unit tests but i'm stuck at this.
This is my service class:
#Component
#Scope("request")
#Path("/subscriber/{accNo}/case/")
public class ServiceRest {
#Context
private UriInfo uriInfo;
#Autowired
private ActivationCase actCase;
#POST
#Path("activation")
public Response activate(#PathParam("accNo") String accNo, InputStream jSonInput) {
Integer order = actCase.activateMethod(accNo);
Link link = new Link("order", uriInfo.getBaseUriBuilder().path(OrderRest.class).path("" + order).build().toString(), "GET");
}
}
This is my Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/myapp-context.xml" })
#WebAppConfiguration
#Ignore
public class ServiceRestTest{
#Autowired
private ServiceRest restClient;
#Autowired
protected OrderServiceImpl orderService;
#Test()
public void testActivationCaseOK() {
UriInfo uriInfo = Mockito.mock(UriInfo.class);
UriBuilder uriBuilder = Mockito.mock(UriBuilder.class);
Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
Mockito.when(orderService.readThings(Mockito.any(Acct.class))).
thenReturn(stuff);
Mockito.when(orderService.maintainPlan(Mockito.any(Order.class))).
thenReturn(stuff2);
Response response = restClient.activateMethod("111111114");
}
Can anyone explain me why is uriInfo NULL?
The mock is done, it should have returned my URIBuilder no?
Thanks for you help

Unfortunately none of suggestions worked for me :/
I have to do a workaround.
So , in order to make this work i moved the #Context private UriInfo uriInfo to be a parameter in each method.
public Response activate(#PathParam("accNo") String accNo, InputStream jSonInput, #Context UriInfo) {
And in my unit test:
Mockito.when(uriInfo.getBaseUriBuilder()).thenReturn(uriBuilder);
Mockito.when(uriBuilder.path(Mockito.anyString())).thenReturn(uriBuilder);
Mockito.when(uriBuilder.build()).thenReturn(uri);
Response response = restClient.activateMethod("111111114", uriInfo);
Thanks for your feedback. I hope this can help somebody in the future

Try adding this code
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
(If it's still null then consider changing the Mockito.mock() to #Mock annotations as described here: http://solutiondesign.com/blog/-/blogs/spring-and-mockito-happy-together )

Related

Spring Boot / Mockito: Mocking RestTemplate but Response Always Null

I'm having problems mocking the response object of my Test Class when using Mockito. I'm trying to test an exception, for this I need one of the attributes of the Class that returns from the POST request. I've successfully mocked the RestTemplate but my when().thenReturn() is not returning anything and I'm getting a null pointer exception at the "if" validation. If anyone could help me on this problem I would be very grateful.
Here is my Service Class:
#Service
public class CaptchaValidatorServiceImpl implements CaptchaValidatorService{
private static final String GOOGLE_CAPTCHA_ENDPOINT = "someEndpoint";
private String stage;
private String captchaSecret;
private RestTemplate restTemplate = new RestTemplate(getClientHttpRequestFactory());
#Override
public void checkToken(String token) throws Exception{
MultiValueMap<String,String> requestMap = new LinkedValueMap<>();
requestMap.add("secret", captchaSecret);
requestMap.add("response", token);
try{
CaptchaResponse response = restTemplate.postForObject(GOOGLE_CAPTCHA_ENDPOINT,
requestMap, CaptchaResponse.class);
if(!response.getSuccess()){
throw new InvalidCaptchaTokenException("Invalid Token");
}
} catch (ResourceAccessException e){
throw new CaptchaValidationNotPossible("No Response from Server");
}
}
private SimpleClientHttpRequestFactory getClientHttpRequestFactory(){
...
}
}
And here is my Test Class:
#SpringBootTest
public class CaptchaValidatorTest{
#Mock
private RestTemplate restTemplate;
#InjectMocks
#Spy
private CaptchaValidatorServiceImpl captchaValidatorService;
private CaptchaResponse captchaResponse = mock(CaptchaResponse.class);
#Test
public void shouldThrowInvalidTokenException() {
captchaResponse.setSuccess(false);
Mockito.when(restTemplate.postForObject(Mockito.anyString(),
ArgumentMatchers.any(Class.class), ArgumentMatchers.any(Class.class)))
.thenReturn(captchaResponse);
Exception exception = assertThrows(InvalidCaptchaTokenException.class, () ->
captchaValidatorService.checkToken("test"));
assertEquals("Invalid Token", exception.getMessage());
}
}
In my opinion it could be a problem with ArgumentMatchers.
Method postForObject require parameters as String, MultiValueMap(or parent) and Class, but you set in Mockito.when: anyString() (correct), any(Class.class) (but MultiValueMap is passed - probably incorrect) and any(Class.class) (correct).
Try use:
Mockito.when(restTemplate.postForObject(ArgumentMatchers.any(String.class),
ArgumentMatchers.any(MultiValueMap.class), ArgumentMatchers.any(Class.class)))
.thenReturn(captchaResponse);
EDIT:
It seems to me that the CaptchaResponse in the test is unnecessarily a mock:
private CaptchaResponse captchaResponse = mock(CaptchaResponse.class);
but if You want this in that way, I think u need to replace:
captchaResponse.setSuccess(false);
to something like:
Mockito.when(captchaResponse.getSuccess()).thenReturn(false);

How could I mock this and make sure to not get back null?

I have the following class that I want to unit test:
#RequiredArgsConstructor
#Service
public class Service {
private final WebClient.Builder builder;
private WebClient webClient;
#PostConstruct
public void init() {
searchUri = "/search-uri";
webClient = builder.baseUrl(searchUri)
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.build();
}
public ResponseSpec search() {
return webClient
.get()
.uri(uriBuilder ->
uriBuilder
.path("/search-uri")
// alot of query param, not important
.build()
)
.accept(MediaType.APPLICATION_JSON)
.acceptCharset(StandardCharsets.UTF_8)
.retrieve();
}
}
This is my test class:
#ExtendWith(MockitoExtension.class)
#RunWith(JUnitPlatform.class)
public class ServiceTest {
#InjectMocks
private Service service;
#Mock
private WebClient webClient;
#Mock
private Builder builder;
#Test
public void testSearch() {
when(builder.baseUrl(Mockito.anyString())).thenReturn(builder);
when(builder.defaultHeader(Mockito.anyString(), Mockito.anyString())).thenReturn(builder);
when(builder.build()).thenReturn(webClient);
issuerServiceImpl.init();
WebClient.RequestHeadersUriSpec uriSpecMock = mock(WebClient.RequestHeadersUriSpec.class);
WebClient.RequestHeadersSpec headersSpecMock = mock(WebClient.RequestHeadersSpec.class);
WebClient.ResponseSpec responseSpecMock = mock(WebClient.ResponseSpec.class);
when(webClient.get()).thenReturn(uriSpecMock);
lenient().when(uriSpecMock.uri(Mockito.any(URI.class))).thenReturn(headersSpecMock);
issuerServiceImpl.searchIssuers("");
}
}
An exception (NullPointerException) happens in the line of .accept(MediaType.APPLICATION_JSON) because the uri() returns null and the code tries to call method .accept() on a null object.
I am not let to change the Service class. I can change only my test class. Not sure how could I make it to work.
Edit: I made it to work, read answers.
So actually I changed this:
lenient().when(uriSpecMock.uri(Mockito.any(URI.class))).thenReturn(headersSpecMock);
to this:
lenient().when(uriSpecMock.uri(Mockito.any(Function.class))).thenReturn(headersSpecMock);
And it works good.
I think this is the problem
when(uriSpecMock.uri(Mockito.any(URI.class)))
you mocked uri to return something when it receives something that's URI class but you're using a lambda function.
The method signature is
S uri(Function<UriBuilder, URI> uriFunction);
Try changing it to
when(uriSpecMock.uri(Mockito.any(Function.class)))
Also check this for future use, quite a good article
https://www.baeldung.com/spring-mocking-webclient

Spring Boot #Async annotation and MockRestServiceServer

I'm using Spring Boot 2.0.6 and Java 10. I did the following service that only hits an external rest api using RestTemplate.
#Service
#Slf4j
public class DbApiClientImpl implements DbApiClient {
private final String URL_DELIMITER = "/";
private RestTemplate restTemplate;
private String url;
public DbApiClientImpl(
RestTemplateBuilder restTemplate,
#Value("${dbapi.namespace}") String namespace,
#Value("${dbapi.url}") String uri,
#Value("${dbapi.username}") String username,
#Value("${dbapi.password}") String password) {
this.restTemplate = restTemplate.basicAuthorization(username,
password).build();
this.url = namespace.concat(uri);
}
#Override
#Async("asyncExecutor")
public Merchant fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return response.getBody();
}
}
And the following test using MockeRestServiceServer:
#RunWith(SpringRunner.class)
#RestClientTest(value = {DbApiClient.class})
public class DbApiClientTest {
private static final String TEST_NAME = "test";
private static final String TEST_NAME_BAD_REQUEST = "test-
1";
private static final String TEST_NAME_SERVER_ERROR =
"test-2";
#Autowired DbApiClient dbApiClient;
#Value("${dbapi.namespace}")
private String namespace;
#Value("${dbapi.url}")
private String dbApiUrl;
#Autowired private MockRestServiceServer mockServer;
#Autowired private ObjectMapper objectMapper;
#Test
public void test() throws
JsonProcessingException, IOException {
Merchant mockMerchantSpec = populateFakeMerchant();
String jsonResponse =
objectMapper.writeValueAsString(mockMerchantSpec);
mockServer
.expect(manyTimes(),
requestTo(dbApiUrl.concat("/").concat(TEST_NAME)))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(jsonResponse,
MediaType.APPLICATION_JSON));
assertNotNull(dbApiClient.fetchMerchant(TEST_NAME));
}
The thing is that I'm getting the following exception when I run the test "No further request expected HTTP GET http://localthost... excecuted"
So seems that the #Async is borking MockerServerService response...
Also, If I commented the #Async annotation everything works just fine and I get all test green.
Thanks in advance for your comments.
Update:
As per #M.Deinum's comment. I removed the CompletableFuture from the service but I'm still getting the same exception.
The problem is your code and not your test.
If you read the documentation (the JavaDoc) of AsyncExecutionInterceptor you will see the mention that only void or Future is supported as a return type. You are returning a plain object and that is internally treated as void.
A call to that method will always respond with null. As your test is running very quickly everything has been teared down already (or is in the process of being teared down) no more calls are expected to be made.
To fix, fix your method signature and return a Future<Merchant> so that you can block and wait for the result.
#Override
#Async("asyncExecutor")
public Future<Merchant> fetchMerchant(String id) {
ResponseEntity<Merchant> response =
restTemplate.getForEntity(url.concat(URL_DELIMITER).concat(id),
Merchant.class);
return CompletableFuture.completedFuture(response.getBody());
}
Now your calling code knows about the returned Future as well as the Spring Async code. Now in your test you can now call get on the returned value (maybe with a timeout to receive an error if something fails). TO inspect the result.

Mocking of Resttemplate failed in spring boot junit

I am writing junit test cases for the method which is call an rest api,following is the code I have tried:
#RunWith(MockitoJUnitRunner.class)
public class NotificationApiClientTests {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private NotificationApiClient notificationApiClient;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(notificationApiClient, "notificationUrl", "myURL***");
}
#Test
public void test_NotificationClickAPI_Call() throws JsonParseException, JsonMappingException, IOException {
ResponseEntity<NotificationClickEvent[]> notificationClickEventList = util.getValidNotificationEvent_ResponseEntity();
Mockito.when(restTemplate.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<NotificationClickEvent[]>> any()
)
).thenReturn(notificationClickEventList);
NotificationClickEvent[] notificationArray = notificationApiClient.requestNotificationClick(Const.NotificationClick, "2018-07-31-10");
assertTrue(notificationArray.length>0);
}
}
and in My NotificationApiClient , it was:
#Value("${notification.base.url}")
private String notificationUrl;
public NotificationApiClient() {
}
public UserInfoEvent[] requestUserInfo(String eventType, String dateStr) {
HttpEntity request = new HttpEntity(setHttpHeaders());
ResponseEntity<UserInfoEvent[]> response = this.exchange(
notificationUrl + eventType + "&dateStr=" + dateStr,
HttpMethod.GET, request, UserInfoEvent[].class);
UserInfoEvent[] userInfoRequest = response.getBody();
return userInfoRequest;
}
but it's not working, as per my code whenever the resttemplate.exchange method is called it should return the notificationClickEventList, But its calling the real api and returns the api result as the list.
Can anyone please help me to solve it?
In your code you are not using restTemplate.exchange method, It seems you are using notificationApiClient's exchange method. So try this.
#Spy
#InjectMocks
private NotificationApiClient notificationApiClient;
Mockito.when(notificationApiClient.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<NotificationClickEvent[]>> any()
)
).thenReturn(notificationClickEventList);

Mock Service inside resource using jersey test framwork

I have a resource for rest API which uses a service.
This service has a constructor with parameters.
I want to test this resource and to mock this service.
This Question: How to pass parameters to REST resource using Jersey 2.5
wasn't helpful because they used #Inject and I cannot use it.
Any suggestions?
The second question is how do I pass parameter to test this resouce:
My code is:
#Path("/2/{subversion: [0-3]}/users")
public class UserResource {
Logger log = Logger.getLogger(UserResource.class);
private MyService service;
public void setService(Service ser) {
this.service = ser;
}
#Context HttpServletRequest currentRequest;
#GET
#Produces("application/json")
public Response getUsers(#Context HttpHeaders httpHeaders, #Context UriInfo
uriInfo) {
// my function
}
}
How can I pass "httpHeaders" and "UriInfo".
My test looks like this:
Response response = target("/2/0/users/").request().get();
Users users = response.readEntity(Users.class);
assertNotNull(users);
For the service, it's good practice to either inject through the constructor or setter. This makes it easy to mock and pass in during unit testing. As for the mocking, you should use a framework like Mockito. Then you can do stuff like
MyService service = Mockito.mock(MyService.class);
when(service.getObject()).thenReturn(new Object());
HttpHeaders headers = Mockito.mock(HttpHeaders.class);
when(headers.getHeaderString("X-Header")).thenReturn("blah");
UriInfo uriInfo = Mockito.mock(UriInfo.class);
when(uriInfo.getRequestUri()).thenReturn(URI.create("http://localhost"));
Then you can just pass all these mocks to your resource class when UNIT testing.
For INTEGRATION testing you won't need to mock the headers or uriinfo. The actual ones will get passed in. But you can still mock the service if you want. Here's an example
public class MockServiceTest extends JerseyTest {
public static interface Service {
String getMessage(String name);
}
#Path("message")
public static class MessageResource {
private final Service service;
public MessageResource(Service service) {
this.service = service;
}
#GET
public String get(#QueryParam("name") String name,
#Context HttpHeaders headers,
#Context UriInfo uriInfo) {
String nameQuery = uriInfo.getQueryParameters().getFirst("name");
String header = headers.getHeaderString("X-Header");
assertNotNull(nameQuery);
assertNotNull(header);
return service.getMessage(name);
}
}
private Service service;
#Override
public ResourceConfig configure() {
service = Mockito.mock(Service.class);
return new ResourceConfig().register(new MessageResource(service));
}
#Test
public void testIt() {
Mockito.when(service.getMessage("peeskillet")).thenReturn("Hello peeskillet");
Response response = target("message").queryParam("name", "peeskillet").request()
.header("X-Header", "blah")
.get();
assertEquals(200, response.getStatus());
assertEquals("Hello peeskillet", response.readEntity(String.class));
}
}

Categories