由于未绑定的 RestTemplate,Spring-Boot RestClientTest 无法正确自动配置 MockRestServiceServer

Posted

技术标签:

【中文标题】由于未绑定的 RestTemplate,Spring-Boot RestClientTest 无法正确自动配置 MockRestServiceServer【英文标题】:Spring-Boot RestClientTest not correctly auto-configuring MockRestServiceServer due to unbound RestTemplate 【发布时间】:2016-12-28 11:32:23 【问题描述】:

编辑:这个问题专门与 spring-boot 1.4.0 中引入的 @RestClientTest 注释有关,该注释旨在替换工厂方法。

问题:

根据documentation,@RestClientTest 应正确配置 MockRestServiceServer 以在测试 REST 客户端时使用。但是,在运行测试时,我收到 IllegalStateException,说 MockServerRestTemplateCustomizer 尚未绑定到 RestTemplate。

值得注意的是,我使用 Gson 进行反序列化,而不是 Jackson,因此排除。

有人知道如何正确使用这个新注释吗?我还没有找到任何需要更多配置的示例。

配置:

@SpringBootConfiguration
@ComponentScan
@EnableAutoConfiguration(exclude = JacksonAutoConfiguration.class)
public class ClientConfiguration 

...

   @Bean
   public RestTemplateBuilder restTemplateBuilder() 
       return new RestTemplateBuilder()
            .rootUri(rootUri)
            .basicAuthorization(username, password);
   

客户:

@Service
public class ComponentsClientImpl implements ComponentsClient 

    private RestTemplate restTemplate;

    @Autowired
    public ComponentsClientImpl(RestTemplateBuilder builder) 
        this.restTemplate = builder.build();
    

    public ResponseDTO getComponentDetails(RequestDTO requestDTO) 
        HttpEntity<RequestDTO> entity = new HttpEntity<>(requestDTO);
        ResponseEntity<ResponseDTO> response = 
              restTemplate.postForEntity("/api", entity, ResponseDTO.class);
        return response.getBody();
    

测试

@RunWith(SpringRunner.class)
@RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest 

    @Autowired
    private ComponentsClient client;

    @Autowired
    private MockRestServiceServer server;

    @Test
    public void getComponentDetailsWhenResultIsSuccessShouldReturnComponentDetails() throws Exception 
        server.expect(requestTo("/api"))
            .andRespond(withSuccess(getResponseJson(), APPLICATION_JSON));

        ResponseDTO response = client.getComponentDetails(requestDto);
        ResponseDTO expected = responseFromJson(getResponseJson());

        assertThat(response, is(expectedResponse));
    

还有例外:

java.lang.IllegalStateException: Unable to use auto-configured MockRestServiceServer since MockServerRestTemplateCustomizer has not been bound to a RestTemplate

答案:

根据下面的答案,不需要将 RestTemplateBuilder bean 声明到上下文中,因为它已经由 spring-boot 自动配置提供。

如果项目是一个 spring-boot 应用程序(它有 @SpringBootApplication 注解),这将按预期工作。然而,在上述情况下,该项目是一个客户端库,因此没有主应用程序。

为了确保在主应用程序上下文中正确注入 RestTemplateBuilder(bean 已被删除),组件扫描需要一个自定义过滤器(@SpringBootApplication 使用的过滤器)

@ComponentScan(excludeFilters = 
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class)
)

【问题讨论】:

【参考方案1】:

您在两个地方都有 RestTemplateBuilder。在 ClientConfiguration 类和 ComponentsClientImpl 类中。 Spring boot 1.4.0 自动配置一个 RestTemplateBuilder ,可用于在需要时创建 RestTemplate 实例。从 ClientConfiguration 类中删除以下代码并运行您的测试。

 @Bean
 public RestTemplateBuilder restTemplateBuilder() 
   return new RestTemplateBuilder()
        .rootUri(rootUri)
        .basicAuthorization(username, password);
  

【讨论】:

您能否进一步详细说明您的意思?如果我删除了 bean 实例化,当在具有常规 spring 上下文(即常规 @SpringBootTest)的代码中使用时,RestTemplateBuilder 是如何注入的?此更改现在通过了我的 @RestClientTest 但其他所有操作都失败了,因为上下文中没有 bean。 在 Spring Boot 1.4.0 应用程序 RestTemplateBuilder 可作为 bean 使用,您可以简单地使用它。 所以这里的关键似乎是 @SpringBootApplication 注释,它提供了您所说的 RestTemplateBuilder 。我的项目中没有这个,因为我正在编写一个客户端库,它的独立测试上下文中没有应用程序上下文。我必须手动为需要它的测试添加它,而 RestClientTest 提供了它。您能否将此详细信息添加到您的答案中,以便为其他人澄清。 “两个地方的构建器”注释非常令人困惑 - 因为它实际上不在两个地方,而是由自动配置提供的。 @abaghel 有没有办法使用配置中的 RestTemplateBuilder 条目来完成这项工作?我需要能够设置全局 RestTemplateBuilder 错误处理程序【参考方案2】:

MockRestServiceServer 实例应使用RestTemplate 从静态工厂构造。测试过程的详细描述见this文章。

在你的例子中,你可以这样做:

@RunWith(SpringRunner.class)
@RestClientTest(ComponentsClientImpl.class)
public class ComponentsClientTest 

    @Autowired
    private ComponentsClient client;

    @Autowired
    private RestTemplate template;

    private MockRestServiceServer server;

    @Before
    public void setUp() 
        server= MockRestServiceServer.createServer(restTemplate);
    

    /*Do your test*/

【讨论】:

这种实例化方法是在 spring-boot 1.4.0 中引入的测试增强功能之前使用的。引入 @RestClientTest 是为了取消该特定的工厂方法。 docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/…

以上是关于由于未绑定的 RestTemplate,Spring-Boot RestClientTest 无法正确自动配置 MockRestServiceServer的主要内容,如果未能解决你的问题,请参考以下文章

RestTemplate使用教程

为啥 RestTemplate 不将响应表示绑定到 PagedResources?

resttemplate转发占用cpu

使用resttemplate访问时,如何使端点仅接受springboot中启用@crossorigin的uri?为啥我没有收到 cors 错误? [复制]

这可能是由于服务端点绑定未使用 HTTP 协议

由于“切入点中的正式未绑定”,Spring AOP BeanCreationException