1. 概述

本文将演示如何为Spring Boot构建的SOAP Web服务编写集成测试。相信你已经掌握应用类的单元测试编写,也熟悉Spring Boot测试的核心概念(可参考Spring Boot测试指南)。因此我们将重点聚焦于使用@WebServiceServerTest注解专门测试Web服务层

2. 测试Spring Web服务

在Spring Web Services框架中,接口(Endpoint)是服务端实现的核心概念。通过特殊的@Endpoint注解标记类作为Web服务接口。这些接口的核心职责是接收XML请求消息、调用业务逻辑并返回响应消息

2.1. Spring Web服务测试支持

测试这类接口时,我们可以简单粗暴地创建单元测试(传入参数或模拟对象)。但这种方法有个致命缺陷:无法真正验证实际传输的XML消息内容。更优方案是创建集成测试来验证消息的XML内容

Spring Web Services 2.0引入了接口集成测试支持。**核心支持类是MockWebServiceClient**。它提供流畅的API,向Spring应用上下文配置的接口发送XML消息,同时可设置响应预期、验证响应XML,完成完整的接口集成测试。

但这种方式会加载整个应用上下文,拖慢测试速度——在追求快速隔离测试特定接口时,这显然不是最佳选择。

2.2. Spring Boot的@WebServiceServerTest

Spring Boot 2.6通过@WebServiceServerTest注解扩展了Web服务测试支持。我们可以用它创建仅聚焦Web服务层的测试,避免加载完整应用上下文。换句话说,它能创建只包含必需@EndpointBean的测试切片,并可用@MockBean模拟依赖项。

这与Spring Boot已有的测试切片注解(如@WebMvcTest@DataJpaTest等)异曲同工。

3. 示例项目搭建

3.1. 依赖配置

鉴于我们已详细讲解过Spring Boot Web服务项目,此处仅添加测试范围所需的spring-ws-test依赖:

<dependency>
    <groupId>org.springframework.ws</groupId>
    <artifactId>spring-ws-test</artifactId>
    <version>4.0.10</version>
    <scope>test</scope>
</dependency>

3.2. 示例Web服务

创建一个简单服务:根据产品ID返回产品数据:

@Endpoint
public class ProductEndpoint {

    @Autowired
    private ProductRepository productRepository;

    @ResponsePayload
    public GetProductResponse getProduct(@RequestPayload GetProductRequest request) {
        GetProductResponse response = new GetProductResponse();
        response.setProduct(productRepository.findProduct(request.getId()));
        return response;
    }
}

这里用@Endpoint注解标记ProductEndpoint组件,注册为XML请求处理器。getProduct方法接收请求对象,从仓库获取产品数据后返回响应。仓库实现细节无关紧要——我们用简单内存实现保持应用简洁,聚焦测试策略。

4. 接口测试

现在创建测试切片,验证Web服务层XML消息的正确处理:

@WebServiceServerTest
class ProductEndpointIntegrationTest {

    @Autowired
    private MockWebServiceClient client;

    @MockBean
    private ProductRepository productRepository;

    @Test
    void givenXmlRequest_whenServiceInvoked_thenValidResponse() throws IOException {
        Product product = createProduct();
        when(productRepository.findProduct("1")).thenReturn(product);

        StringSource request = new StringSource(
          "<bd:getProductRequest xmlns:bd='http://baeldung.com/spring-boot-web-service'>" + 
            "<bd:id>1</bd:id>" + 
          "</bd:getProductRequest>"
        );
        
        StringSource expectedResponse = new StringSource(
          "<bd:getProductResponse xmlns:bd='http://baeldung.com/spring-boot-web-service'>" + 
            "<bd:product>" + 
              "<bd:id>1</bd:id>" + 
              "<bd:name>Product 1</bd:name>" + 
            "</bd:product>" + 
          "</bd:getProductResponse>"
        );

        client.sendRequest(withPayload(request))
          .andExpect(noFault())
          .andExpect(validPayload(new ClassPathResource("webservice/products.xsd")))
          .andExpect(payload(expectedResponse))
          .andExpect(xpath("/bd:getProductResponse/bd:product[1]/bd:name", NAMESPACE_MAPPING)
            .evaluatesTo("Product 1"));
    }
}

此测试仅配置应用中带@Endpoint注解的Bean。该测试切片创建了一个精简的应用上下文,帮助我们构建快速精准的集成测试,避免重复加载完整上下文的性能开销。

关键点:该注解会自动配置MockWebServiceClient及相关组件。我们可直接注入此客户端发送getProductRequest XML请求,并通过流式断言验证结果。这些断言会验证:

  1. 响应XML符合指定XSD模式
  2. 响应XML与预期匹配
  3. 可用XPath表达式提取和比较响应XML中的值

4.1. 接口协作者

示例中我们用@MockBean模拟ProductEndpoint所需的仓库。若无此模拟,应用上下文将无法启动(因完整自动配置被禁用)。测试框架不会在执行前配置任何@Component@Service@Repository Bean

若需真实协作者而非模拟对象,可用@Import声明。Spring会自动查找并注入这些类到接口中。

4.2. 加载完整上下文

如前所述,@WebServiceServerTest不会加载完整应用上下文。若测试需要完整上下文,应组合使用@SpringBootTest@AutoConfigureMockWebServiceClient。此后可按类似方式发送请求并验证响应。

5. 总结

本文探讨了Spring Boot引入的@WebServiceServerTest注解。我们首先概述了Web服务应用的测试支持,随后演示如何使用该注解创建Web服务层测试切片,构建快速精准的集成测试。

完整源码见GitHub仓库


原始标题:Spring Web Service Integration Tests with @WebServiceServerTest

» 下一篇: Java Weekly, 第440期