简体   繁体   中英

How to Mock Spring RestTemplate using mockito?

Actually I am trying to do UT of bellow method :

    @Override
public Reservation findReservation(Long id) {
    RestTemplate restTemplate = new RestTemplate();
    Reservation reservation = restTemplate.getForObject(RESERVATION_REST_URL+id, Reservation.class);
    return reservation;
}

I am doing UT as bellow :

@Before
public void setUp(){
  MockitoAnnotations.initMocks(this);
}

@Test
public void testFindReservation(){
    Reservation reservation = new Reservation();
    reservation.setId(1000l);
    reservation.setCheckiIn(true);
    reservation.setNumberOfBags(10);

 when(restTemplate.getForObject("http://localhost:8080/flightreservation/reserva 
 tions/1000", Reservation.class))
            .thenReturn(reservation);

   Reservation res =  reservationRestClient.findReservation(1000l);

   Assert.assertNotNull(res);
}

But it gives error, As per my understanding its not mocked properly, Somehow RestTemplate is trying to call real api rather than mock.

ResourceAccessException: I/O error on GET request for http://localhost:8080/flightreservation/reserva 
     tions/1000

Service class :-

    @Service
public class ReservationRestServiceImpl implements ReservationRestService {
    private static final String RESERVATION_REST_URL = "http://localhost:8080/flightreservation/reservations/";
    private final RestTemplate restTemplate;

    public ReservationRestServiceImpl(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    @Override
    public Reservation fetchReservationByPnr(Long id) {

        Reservation reservation = restTemplate.getForObject(RESERVATION_REST_URL+id, Reservation.class);
        return reservation;
    }
}

And Test File is as bellow, After Mocking RestTemplateBuilder In testfile, It gives NLP.

    @RunWith(SpringRunner.class)
//@AutoConfigureWebClient
@SpringBootTest(classes = { WebcheckinApplication.class })
public class WebcheckinApplicationTests {


    private ReservationRestServiceImpl reservationRestServiceImpl;

    @Mock
    private RestTemplateBuilder restTemplateBuilder;

    @Before
    public void setUp() throws Exception {
        reservationRestServiceImpl = new ReservationRestServiceImpl();
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void contextLoads() {
    }


    @Test
    public void testFindReservation(){
        Reservation reservation = new Reservation();
        reservation.setId(1000l);
        reservation.setCheckiIn(true);
        reservation.setNumberOfBags(10);

        RestTemplateBuilder builder = mock(RestTemplateBuilder.class);
        reservationRestServiceImpl.setBuilder(builder);
        reservationRestServiceImpl.init();

        when(builder.build().getForObject("http://localhost:8080/flightreservation/reservations/1000", Reservation.class))
                .thenReturn(reservation);

       Reservation res =  reservationRestServiceImpl.fetchReservationByPnr(1000l);

       Assert.assertNotNull(res);

        //assertEquals("{message : 'under construction'}", result);
    }
}

I'm not sure what version of Spring you are using. However the recommended pattern in Spring is to use a RestTemplateBuilder instead of creating a RestTemplate directly

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-resttemplate.html

A RestTemplateBuilder would be injected into the service, and the rest template constructed from it. In your test case, you can inject a Mocked RestTemplateBuilder which can, in turn, return a mocked RestTemplate.

So, if you have the ability to refactor the original service, you can have some code that is much more testable.

--More Details--

This is how I would set up the class...

public class SomeService {

    private RestTemplateBuilder builder;
    private RestTemplate restTemplate;

    @Autowired
    public void setBuilder(RestTemplateBuilder builder) {
        this.builder = builder;
    }

    @PostConstruct
    public void init() {
        restTemplate = builder.build();
    }

    public Object fetchReservationByPnr(Long id) {
        return restTemplate.getForObject("someUrl"+id, Object.class);
    }
}

Then, in your test, you can just create the service, assign a Mocked RestTemplateBuilder and call init. You won't need the Spring runner or run up the test in a Spring Context. The standard JUnit runner will suffice.

I haven't completely done your test, but this works for me

import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.client.RestTemplate;

public class SomeServiceTest {

    @Test
    public void testMe() {
        RestTemplateBuilder mockedBuilder = Mockito.mock(RestTemplateBuilder.class);
        RestTemplate mockedRestTemplate = Mockito.mock(RestTemplate.class);
        Mockito.when(mockedBuilder.build()).thenReturn(mockedRestTemplate);

        SomeService someService = new SomeService();
        someService.setBuilder(mockedBuilder);
        someService.init();

        Mockito.verify(mockedBuilder).build();
    }
}

For your test, simply add additional mocks to the mocked RestTemplate

@Sandeep Tiwari..make your test case set up like this ...

@Autowired
    private MockRestServiceServer server;

    @Autowired
    private RestTemplate restTemplate;

add these following in @Before method ....
------------------------------------
server.expect(ExpectedCount.manyTimes(), requestTo(URL)).andRespond(withSuccess(detailsString, MediaType.APPLICATION_JSON));

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