Spring Boot中@RestClientTest快速指南

2023/05/11

1. 简介

本文是对@RestClientTest注解的快速介绍。

新注解有助于简化和加快Spring应用程序中REST客户端的测试。

2. Spring Boot Pre-1.4中的REST Client支持

Spring Boot是一个方便的框架,它提供了许多具有典型设置的自动配置的Spring bean,使你可以减少对Spring应用程序配置的关注,而将更多精力放在代码和业务逻辑上。

但是在1.3版本中,当我们想要创建或测试REST服务客户端时,我们并没有得到很多帮助,它对REST客户端的支持不是很深入。

要为REST API创建客户端-通常使用RestTemplate实例。通常必须在使用前对其进行配置,并且其配置可能会有所不同,因此Spring Boot不提供任何通用配置的RestTemplate bean。

测试REST客户端也是如此。在Spring Boot 1.4.0之前,测试Spring REST客户端的过程与测试任何其他基于Spring的应用程序没有太大区别。你将创建一个MockRestServiceServer实例,将其绑定到测试中的RestTemplate实例,并为其提供对请求的模拟响应,如下所示:

RestTemplate restTemplate = new RestTemplate();

MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(requestTo("/greeting"))
      .andRespond(withSuccess());

// Test code that uses the above RestTemplate ...

mockServer.verify();

你还必须初始化Spring容器并确保仅将需要的组件加载到上下文中,以加快上下文加载时间(从而加快测试执行时间)。

3. Spring Boot 1.4+中新的REST客户端特性

在Spring Boot 1.4中,团队为简化和加快REST客户端的创建和测试做出了扎实的努力。

因此,让我们看看新功能。

3.1 将Spring Boot添加到你的项目中

首先,你需要确保你的项目使用的是Spring Boot 1.4.x或更高版本:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

最新发布版本可在此处找到。

3.2 RestTemplateBuilder

Spring Boot带来了自动配置的RestTemplateBuilder来简化RestTemplates的创建,以及匹配的@RestClientTest注解来测试使用RestTemplateBuilder构建的客户端。以下是如何创建一个简单的REST客户端,并为你自动注入RestTemplateBuilder:

@Service
public class DetailsServiceClient {

    private final RestTemplate restTemplate;

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

    public Details getUserDetails(String name) {
        return restTemplate.getForObject("/{name}/details", Details.class, name);
    }
}

请注意,我们没有明确地将RestTemplateBuilder实例注入到构造函数,这要归功于一个名为隐式构造函数注入的新Spring特性,本文对此进行了讨论。

RestTemplateBuilder提供了方便的方法来注册消息转换器、错误处理程序、URI模板处理程序、基本授权,还可以使用你需要的任何其他自定义程序。

3.3 @RestClientTest

为了测试这样一个用RestTemplateBuilder构建的REST客户端,你可以使用用@RestClientTest标注的SpringRunner执行的测试类。此注解禁用完全自动配置,并且仅应用与REST客户端测试相关的配置,即Jackson或GSON自动配置和@JsonComponent beans,但不适用于常规@Component beans。

@RestClientTest确保自动配置Jackson和GSON支持,并将预配置的RestTemplateBuilder和MockRestServiceServer实例添加到上下文中。被测bean由@RestClientTest注解的value或components属性指定:

@ExtendWith(SpringExtension.class)
@RestClientTest({DetailsServiceClient.class})
class DetailsServiceClientIntegrationTest {

    @Autowired
    private DetailsServiceClient client;

    @Autowired
    private MockRestServiceServer server;

    @Autowired
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() throws Exception {
        String detailsString = objectMapper.writeValueAsString(new Details("John Smith", "john"));
        this.server.expect(requestTo("/john/details"))
              .andRespond(withSuccess(detailsString, MediaType.APPLICATION_JSON));
    }

    @Test
    void whenCallingGetUserDetails_thenClientExecutesCorrectCall() throws Exception {
        Details details = this.client.getUserDetails("john");

        assertThat(details.getLogin()).isEqualTo("john");
        assertThat(details.getName()).isEqualTo("John Smith");
    }
}

首先,我们需要通过添加@ExtendWith(SpringExtension.class)注解来确保此测试使用SpringExtension运行。

那么,有什么新变化呢?

首先-@RestClientTest注解允许我们指定被测试的确切服务,在我们的例子中它是DetailsServiceClient类。此服务将加载到测试上下文中,而其他所有内容都将被过滤掉。

这允许我们在测试中自动装配DetailsServiceClient实例,并将其他所有内容留在测试之外,从而加快上下文的加载。

其次-由于MockRestServiceServer实例也配置为@RestClientTest注解测试(并为我们绑定到DetailsServiceClient实例),我们可以简单地注入它并使用。

最后-对@RestClientTest的JSON支持允许我们注入Jackson的ObjectMapper实例来准备MockRestServiceServer的mock答案值。

剩下要做的就是执行对我们服务的调用并验证结果。

4. 总结

在本文中,我们讨论了新的@RestClientTest注解,它允许轻松快速地测试使用Spring构建的REST客户端。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章