【问题标题】:How to Mock Spring RestTemplate using mockito?如何使用 mockito 模拟 Spring RestTemplate?
【发布时间】:2018-08-31 10:19:26
【问题描述】:

其实我正在尝试做 UT 的波纹管方法:

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

我正在做 UT,如下所示:

@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);
}

但它给出了错误,据我了解,它没有正确模拟,不知何故 RestTemplate 试图调用真正的 api 而不是模拟。

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

服务类:-

    @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;
    }
}

而Test File如下,在testfile中Mocking RestTemplateBuilder后,给出了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);
    }
}

【问题讨论】:

    标签: spring spring-boot mockito spring-test


    【解决方案1】:

    @Sandeep Tiwari..让你的测试用例设置成这样......

    @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));
    

    【讨论】:

      【解决方案2】:

      我不确定您使用的是哪个版本的 Spring。但是 Spring 中推荐的模式是使用 RestTemplateBuilder 而不是直接创建 RestTemplate

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

      一个 RestTemplateBuilder 将被注入到服务中,其余模板由它构建。在您的测试用例中,您可以注入一个 Mocked RestTemplateBuilder,它又可以返回一个模拟的 RestTemplate。

      因此,如果您有能力重构原始服务,您可以拥有一些更可测试的代码。

      --更多细节--

      这就是我设置课程的方式...

      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);
          }
      }
      

      然后,在您的测试中,您可以创建服务,分配一个模拟的 RestTemplateBuilder 并调用 init.您不需要 Spring runner 或在 Spring Context 中运行测试。标准的 JUnit 运行器就足够了。

      我还没有完全完成你的测试,但这对我有用

      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();
          }
      }
      

      对于您的测试,只需将其他模拟添加到模拟的 RestTemplate

      【讨论】:

      • 嗨 @EdH 我现在已经更新了源代码,但是它提供了 NLP
      • 是的,我看到了更新。但是,测试代码不应构建其余模板。我将在上面的答案中提供更多详细信息
      • 可以在测试电话中提供帮助,实际上我仍然面临同样的问题 NLP,实际上我已经与您在测试文件和 setBuilder 中的更改和自动装配服务保持一致。也更新了测试文件
      • 它给了我 NLP,在模拟之后,不知何故无法模拟 RestTemplateBuilder。
      • 嗨@SandeepTiwari,我已经提供了更多细节。请参考我是如何完成测试的。这在本地对我有用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-13
      • 1970-01-01
      • 2019-06-06
      相关资源
      最近更新 更多