0

I am trying to mock this method with postForEntity call -


public AuthorizeClient(RestTemplateBuilder builder, Config config) {
    this.grantedUrl = config.grantedUrl();
    this.restTemplate = HttpClientHelper.getRestTemplate(builder, authorizationConfig);
  }

private final RestTemplate restTemplate;
private String grantedUrl;
public List<Permission> getPermissions(
          PermissionsRequest permissionsRequest) {
    try {
      var headers = new HttpHeaders();
      var request = new HttpEntity<PermissionsRequest>(permissionsRequest, headers);
      var permissions = restTemplate.postForEntity(grantedUrl, request, Permission[].class);
      return Arrays.asList(permissions.getBody());
    } catch (HttpClientErrorException err) {
      logger.error(err);
      throw err;
    }
  }

Here is my test case -

RestTemplateBuilder restTemplateBuilder =  new RestTemplateBuilder();

Config config = new Config();

@InjectMocks
AuthorizeClient authorizeClient = new AuthorizeClient(restTemplateBuilder, config);

@Mock
private RestTemplate restTemplate;

PermissionsRequest permissionsRequest;

ResponseEntity<Permission[]> expGrantedPermissions;

@Test
    public void testAuthorizationPermissions()  {

        when(restTemplate.postForEntity(anyString(), any(), eq(Permission[].class))).thenReturn(expGrantedPermissions);

        var res = authorizeClient.getAllGrantedPermissions(permissionsRequest);
        assertNotNull(res);

    }

I'm getting this error. Looks like mock is not created properly ..

java.lang.IllegalArgumentException: URI is not absolute

At this line -

var res = authorizeClient.getPermissions(permissionsRequest);

My AuthorizeClient is constructed like above..

Please suggest what am I missing.

Thanks in advance

Sshh
  • 63
  • 2
  • 10
  • @Nikolas please suggest – Sshh Dec 15 '19 at 16:17
  • You can't use @InjectMocks and new at the same time. InjectsMocks won't work in this case. Just set all your fields manually or create a default constructor in order all dependencies will be injected automatically. – Evgeniy Dec 15 '19 at 19:33
  • Thanks @Evgeniy do you have any working example? – Sshh Dec 15 '19 at 19:46
  • @second ok, but still it's not correct to use in this way. Also, I wouldn't use "any" for tests at least I'm trying to avoid this. It makes the case too general. I would move new HttpHeaders() and new HttpEntity to protected "create" methods in AuthorizeClient class and mock it with Spy annotation – Evgeniy Dec 15 '19 at 19:46
  • Thanks to you too @second. Do you have any working example? I tried just now and it seems I have to add more code.. – Sshh Dec 15 '19 at 19:46
  • I can't use power mockito though – Sshh Dec 15 '19 at 20:01

1 Answers1

1

From your example code I don't see a relation between your restTemplate mock and the AuthorizeClient class.

The problem is that your restTemplate field in your object is final. In this case - even so the @InjectMocks generally works with the new constructor - injection of the mock does not happen.

You might want to add the full stacktrace, but I would assume that config.grantedUrl() does not return a valid url.


I assume grantedUrl is a String. In that case you define behaviour for the wrong method. There is only a 4-param version of postForEntity, so you'll need to define the mock for that using Mockito.<Object>any() for the varargs parameter.


To fix this: You might want to mock the RestTemplateBuilder and define behaviour for the methods that are used by HttpClientHelper.getRestTemplate.

You might also want to consider using PowerMockito to mock the static method directly.

Alternatively you could refactor your code to pass the RestTemplate directly to the constructor instead of passing the builder. At least in your example the builder does not seem to be used for anything else within the class, so I would consider it a removable dependency.

Also using Constructor injection is considered the way to go by many.


I assume PermissionsRequest andPermission` are your classes, so I can't really test this specific case, but basically this should work:

Note that I assume a changed constructor for AuthorizeClient that accepts both config and restTemplate. Instead of using the annotation I setup the mock manually, because you used a real Config object in your example. If mocking both is an option, you can still use the @InjectMocks annotation.

@RunWith(MockitoJUnitRunner.class)
public class RestTemplateTest {

    Config config = new Config();

    @Mock
    private RestTemplate restTemplate;

    PermissionsRequest permissionsRequest;
    ResponseEntity<Permission[]> expGrantedPermissions;

    @Test
    public void testAuthorizationPermissions()  {

        // init permissionsRequest and expGrantedPermissions, if you haven't done that
        AuthorizeClient authorizeClient = new AuthorizeClient(config, restTemplate);

        Mockito.when(restTemplate.postForEntity(Mockito.anyString(), Mockito.any(), Mockito.eq(Permission[].class), Mockito.<Object>any())).thenReturn(expGrantedPermissions);

        List<Permission> res = authorizeClient.getAllGrantedPermissions(permissionsRequest);
        assertNotNull(res);
    }
}

Ps.:
For a standalone example of a different method on restTemplate you can check my answer here. This at least can help you verify that you mock the correct method.

second
  • 4,069
  • 2
  • 9
  • 24
  • Thanks for that @second I'm getting java.lang.NullPointerException on this line - Mockito.when(restTemplate.postForEntity(Mockito.anyString(), Mockito.any(), Mockito.eq(Permission[].class), Mockito.any())).thenReturn(expGrantedPermissions); – Sshh Dec 15 '19 at 20:21
  • Can you point out what exactly is null for you? I've added the surrounding class to my example, as well as the note that you'll probably want to initialize your permission variables. – second Dec 15 '19 at 20:25
  • I initialized my PermissionsRequest in beforeEach. If I initialize RestTemplate like @Mock private RestTemplate restTemplate = new RestTemplate(); I see my original error like "URI is not absolute".. I see null pointer is coming from RestTemplate.. Am I missing anything? – Sshh Dec 15 '19 at 20:35
  • You should not initialize the `@Mock` field by yourself. You want mockito to create the mock for you. You might want to update your question with your current code. – second Dec 15 '19 at 20:37
  • Yeah then going back to same error.. Null pointer exception on when.. – Sshh Dec 15 '19 at 20:39
  • I linked an older an older [`answer`](https://stackoverflow.com/questions/57381132/mocking-overloaded-methoods-with-mockito/57394411#57394411) of mine. You can adjust the standalone example there to your method, and check that its working with the parameters you are using. I assume the problem you are facing is still with the injection of the mock. – second Dec 15 '19 at 20:41
  • What java and mockito version are you using? – second Dec 15 '19 at 20:43
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204311/discussion-between-second-and-sshh). – second Dec 15 '19 at 20:43