简体   繁体   中英

How to write Mockito Junit test cases for Rest template?

From the service I call the third party api using RestTemplate.

@RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {
@InjectMocks
private ForceService forceService;
@Mock
private RestTemplate restTemplate;
@Before
public void setup() {
    forceService = new ForceService(config, restTemplate);
}
@Test
public void createTest_valid() throws JSONException {
    /*Mockito.when(restTemplate.exchange(url, HttpMethod.POST, entity, CreateRecordResult.class))
    .thenReturn(response);*/
     Mockito.verify(restTemplate, Mockito.times(1))
    .exchange(Mockito.anyString(),
                    Mockito.<HttpMethod> any(),
                    Mockito.<HttpEntity<?>> any(),
                    Mockito.<Class<?>> any());
    forceService.createLead(lead);
}
}

I tried using both the any() method and directly specifying the values. Directly specifying the value in entity seems to be not the right way to test. Below is the service class that I need to write test cases for.

@Component
public class ForceService {
    private RestTemplate restTemplate;
public ForceService(ForceServiceConfig config,  RestTemplate restTemplate) {
    this.config = config;
    this.restTemplate = restTemplate;
}
public String createLead(Lead lead) {
    HttpHeaders headers = new HttpHeaders();
    headers.set(AUTHORIZATION, getAccessToken());
    headers.set(ACCEPT, APPLICATION_JSON);
    headers.set(CONTENT_TYPE, APPLICATION_JSON);
    LeadWrap leadWrap = new LeadWrap();
    leadWrap.setFirstName(lead.getFirstName());
    leadWrap.setLastName(lead.getLastName());
    leadWrap.setEmail(lead.getEmail());
    leadWrap.setPhone(lead.getPhone());

    String jsonString;
    try {
        jsonString = new ObjectMapper().writeValueAsString(leadWrap);

    } catch (IOException e) {
        throw new RuntimeException(e);
    }
    HttpEntity<String> entity = new HttpEntity<>(jsonString, headers);

    ResponseEntity<CreateRecordResult> exchange = restTemplate.exchange(
            config.restUrl + "/v" + config.restVersion + "/sobjects/Lead/", HttpMethod.POST, entity,
            CreateRecordResult.class);
    if (exchange.getStatusCode().equals(HttpStatus.CREATED)) {
        if (exchange.getBody() != null && exchange.getBody().success) {
            LOGGER.info("Lead record created with Id " + exchange.getBody().id);
            return exchange.getBody().id;
        }
        throw new RuntimeException("Record is not created");
    } else {
        LOGGER.error(RETURN_STATUS + exchange.getStatusCode());
        throw new RuntimeException(RETURN_STATUS + exchange.getStatusCode());
    }

The above test case returns the ResponseEntity exchange as null. Is there any solution for this to make the test case work for RestTemplate exchange call?

The verify needs to go after the call to the production code, in your case the createLead() call. You also are going to want to use matchers for your when call, which probably shouldn't be commented out. In cases like yours you generally don't need both the when and the verify. It just makes the test more complex and harder to read.

I use the verify if there is no return from the service call that I can assert on. In those cases I would wrap all the parameters of the when (if needed to get past a null pointer exception or other error) in any() such as any(HttpEntity.class) or anyString() so the params aren't ambiguous. Then you can use the verify to confirm the actual params are correct. This strategy is easier to maintain. Unfortunately, often it requires an argument captor to verify headers or other params are sent correctly. I say it's unfortunate because the tests become big and messy,

If I can assert on the result I often just use the when. In that case I would wrap the params with eq() , such as eq(httpEntity) . In that case the HttpEntity class would need to have a good .equals() method or it would just use the default and probably isn't very helpful. But, it is generally pretty powerful.

You shouldn't use @InjectMocks and initialize in the setup. If you @InjectMocks it creates the instance and injects the mocks. You seem to wan to put a real config in so you would use the setup method or you could mock the config. I used a correct matcher, but you might have to refine them, for example switch some any() to eq() to truly test what you want to test. I also reordered so the action or the call to production call is before the verify. This test should get you started.

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class ForceServiceTest {

    private ForceService forceService;
    @Mock
    private RestTemplate restTemplate;

    @Before
    public void setup() {
        forceService = new ForceService(new ForceServiceConfig(), restTemplate);
    }

    @Test
    public void createTest_valid() throws Exception {
        when(restTemplate.exchange(anyString(), eq(HttpMethod.POST),
                any(HttpEntity.class),
                eq(CreateRecordResult.class)))
                .thenReturn(new ResponseEntity<>(new CreateRecordResult(), HttpStatus.CREATED));

        forceService.createLead();

        verify(restTemplate, times(1))
                .exchange(eq("config.restUrl/vconfig.restVersion/sobjects/Lead/"),
                        any(HttpMethod.class),
                        any(HttpEntity.class),
                        eq(CreateRecordResult.class));
    }
}

You need to tell Mockito what to return when the mock is called...

when(restTemplate.exchange(anyString(), any(), any(), any())).thenReturn(...

Insert the responseEntity you want returned from the call to exchange in the thenReturn.

@DCTID code saved my day. Along with that I faced the below issue and fixed it. To mock the ResponseEntity's body, I created an object and set value to it. Else, it was not passing this condition - if (exchange.getBody() != null && exchange.getBody().success)

CreateRecordResult createRecordResult = new CreateRecordResult();
createRecordResult.success = true;
Mockito.when(restTemplate.exchange(anyString(), eq(HttpMethod.POST), any(HttpEntity.class),
            eq(CreateRecordResult.class)))
                    .thenReturn(new ResponseEntity<>(createRecordResult, HttpStatus.CREATED));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM