用于单元测试的模拟休息模板

Posted

技术标签:

【中文标题】用于单元测试的模拟休息模板【英文标题】:mock rest template for unit test 【发布时间】:2019-07-27 18:02:06 【问题描述】:

我想在 Spring Boot 中模拟 RestTemplate,我在方法中进行 REST 调用。为了测试我正在创建的微服务的控制器, 我想在我的微服务控制器中测试方法。

例如:

@GetMapping(value = "/getMasterDataView", produces =  MediaType.APPLICATION_JSON_VALUE )
@CrossOrigin(origins =  "http://192.1**********" , maxAge = 3000)
public ResponseEntity<MasterDataViewDTO> getMasterDataView() throws IOException 

    final String uri = "http://localhost:8089/*********";

    RestTemplate restTemplate = new RestTemplate();
    MasterDataViewDTO masterDataViewDTO = restTemplate.getForObject(uri, MasterDataViewDTO.class);

    return new ResponseEntity<>(masterDataViewDTO, HttpStatus.OK);


如何使用模拟来测试这个?

这是我目前所拥有的:

@Test
    public void testgetMasterDataView() throws IOException 

    MasterDataViewDTO masterDataViewDTO= mock(MasterDataViewDTO.class);
    //String uri = "http://localhost:8089/*********"; 

    Mockito.when(restTemplate.getForObject(Mockito.anyString(),ArgumentMatchers.any(Class.class))).thenReturn(masterDataViewDTO);

    assertEquals("OK",inquiryController.getMasterDataView().getStatusCode());        

我在运行模拟时遇到错误,方法 getMasterDataView() 被调用,其中的 REST 调用也被调用并引发错误。如何编写测试以便不调用 REST 端点?如果可能的话,我想用 Mockito 来做这个。

【问题讨论】:

是否有可能使用 mockito 来做到这一点。建议我们在项目中使用 mockito 进行测试 【参考方案1】:

在开始编写测试之前,您应该稍微更改一下代码。首先,如果您提取 RestTemplate,并为它创建一个单独的 bean,您将在控制器中注入它会容易得多。

为此,请在 @Configuration 类或主类中添加类似内容:

@Bean
public RestTemplate restTemplate() 
    return new RestTemplate();

此外,您必须从控制器中删除 new RestTemplate(),并改为自动装配它,例如:

@Autowired
private RestTemplate restTemplate;

既然您已经这样做了,那么在您的测试中注入一个模拟 RestTemplate 会容易得多。

对于您的测试,您有两种选择:

    使用模拟框架(例如 Mockito)模拟 RestTemplate 和您尝试访问的所有方法 或者您可以使用MockRestServiceServer,它允许您编写测试来验证 URL 是否被正确调用、请求是否匹配等等。

使用 Mockito 进行测试

要使用 Mockito 模拟您的 RestTemplate,您必须确保将以下注释添加到您的测试中:

@RunWith(MockitoJUnitRunner.class)

之后,您可以这样做:

@InjectMocks
private MyController controller;
@Mock
private RestTemplate restTemplate;

现在您可以像这样调整您的测试:

@Test
public void testgetMasterDataView() throws IOException 
    MasterDataViewDTO dto = new MasterDataViewDTO();
    when(restTemplate.getForObject("http://localhost:8089/*********", MasterDataViewDTO.class)).thenReturn(dto);
    ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    assertThat(response.getBody()).isEqualTo(dto);

您可以像在测试中那样模拟 DTO,但您不必这样做,而且我认为这样做没有任何好处。你必须模拟的是restTemplate.getForObject(..) 调用。

使用MockRestServiceServer 进行测试

另一种方法是使用MockRestServiceServer。为此,您必须在测试中使用以下注释:

@RunWith(SpringRunner.class)
@RestClientTest

然后你必须自动连接你的控制器和MockRestServiceServer,例如:

@Autowired
private MyController controller;
@Autowired
private MockRestServiceServer server;

现在您可以编写如下测试:

@Test
public void testgetMasterDataView() throws IOException 
    server
        .expect(once(), requestTo("http://localhost:8089/*********"))
        .andExpect(method(HttpMethod.GET))
        .andRespond(withSuccess(new ClassPathResource("my-mocked-result.json"), MediaType.APPLICATION_JSON));
    ResponseEntity<MasterDataViewDTO> response = controller.getMasterDataView();
    assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    // TODO: Write assertions to see if the DTO matches the JSON structure

除了测试您的实际 REST 调用是否匹配之外,这还允许您测试您的 JSON-to-DTO 是否也能正常工作。

【讨论】:

如果我自动装配其余模板,将是一个单例实例吗?在所有请求之间共享 @ApurvAdarsh 是的,会的。 如果对同一个实例进行了多次调用,是否会影响单例调用的响应@g00glen00b 不,它不会影响响应。自动装配 resttemplate 是最佳实践。【参考方案2】:

您可以通过使用@RestClientTestMockRestServiceServer 来实现此目的。 their documentation中提供的示例:

@RunWith(SpringRunner.class)
@RestClientTest(RemoteVehicleDetailsService.class)
public class ExampleRestClientTest 

    @Autowired
    private RemoteVehicleDetailsService service;

    @Autowired
    private MockRestServiceServer server;

    @Test
    public void getVehicleDetailsWhenResultIsSuccessShouldReturnDetails()
            throws Exception 
        this.server.expect(requestTo("/greet/details"))
                .andRespond(withSuccess("hello", MediaType.TEXT_PLAIN));
        String greeting = this.service.callRestService();
        assertThat(greeting).isEqualTo("hello");
    


【讨论】:

我不想加载完整的上下文,因为我不想使用数据库 是否有可能使用 mockito 来做到这一点。建议我们在项目中使用 mockito 进行测试 @Apurv Adarsh,是的,你可以。请参考 g00glen00b 的回答。

以上是关于用于单元测试的模拟休息模板的主要内容,如果未能解决你的问题,请参考以下文章

单元测试 Freemarker 模板

用于 RowMapper 的 Spring jdbc 模板的单元测试用例

Spark scala 模拟 spark.implicits 用于单元测试

向下转换/子类 UIViewController 用于单元测试中的模拟

测试单元 Spring boot:无法注册模拟 bean

Spring单元测试休息控制器