51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

使用 @WebServiceServerTest 进行 Spring Web Service 集成测试

1、简介 {#1简介}

本文将带你了解如何使用 @WebServiceServerTest 为 Spring Boot 开发的 SOAP Web Service 编写集成测试。

2、测试 Spring Web Service {#2测试-spring-web-service}

在 Spring Web Service 中,端点是服务器端 Service 实现的关键概念。专门的 @Endpoint 注解将类标记为 Web Service 端点。这些端点负责接收 XML 请求消息、调用所需的业务 Service 并将结果作为响应消息返回。

2.1、Spring Web Service 的测试支持 {#21spring-web-service-的测试支持}

为了测试此类端点,我们可以通过传递所需的参数或模拟(Mock)来轻松创建单元测试。然而,这样做的主要缺点是无法实际测试通过网络发送的 XML 消息的内容。另一种方法是创建集成测试,以验证消息的 XML 内容。

Spring Web Service 2.0 引入了对此类端点集成测试的支持。提供这种支持的核心类是 MockWebServiceClient。它提供了一个 Fluent API,用于向 Spring Application Context 中配置的适当端点发送 XML 消息。此外,我们还可以设置响应预期、验证响应 XML,并对端点执行完整的集成测试。

不过,这需要调用整个 Application Context,从而减慢测试执行速度。这通常是不可取的,尤其是当我们希望为特定 Web Service 端点创建快速、隔离的测试时。

2.2、Spring Boot @WebServiceServerTest {#22spring-boot-webserviceservertest}

Spring Boot 2.6 通过 @WebServiceServerTest 注解扩展了 Web Service 测试支持。

我们可以将其用于仅关注 Web Service 而非加载整个 Application Context 的测试。换句话说,我们可以创建一个只包含所需 @Endpoint Bean 的测试片段,并使用 @MockBean 来模拟任何依赖。

这与 Spring Boot 已经提供的便捷测试片段注解(如 @WebMvcTest@DataJpaTest 等)非常相似。

3、建立示例项目 {#3建立示例项目}

3.1、依赖 {#31依赖}

除了 Web Service 必须依赖外,还需要额外的 spring-ws-test 依赖(test scope):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
</dependency>

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

3.2、Web Service 示例 {#32web-service-示例}

接下来,创建一个简单的服务,为指定的 Product ID 返回一些 Product 数据:

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

这里,我们用 @EndpointProductEndpoint 组件进行了注解,从而将其注册用于处理相应的 XML 请求。

getProduct 方法接收 request 对象,并从 Repository 中获取 Product 数据,然后返回响应。Repository 的细节在这里并不重要。在本例中,为了简单,可以使用一个简单的内存实现。

4、端点测试 {#4端点测试}

最后,创建一个测试片段,验证 Web Service 对 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。换句话说,这个测试片段创建了一个精简的 Application Context。这帮助我们构建有针对性且快速的集成测试,而不会因为反复加载整个 Application Context 而影响性能。

重要的是,该注解还配置了一个 MockWebServiceClient 以及其他相关的自动配置。因此,我们可以将该客户端注入到我们的测试中,并用它来发送 getProductRequest XML 请求,然后进行各种 Fluent 式的期望操作。

预期(Expectation)验证响应 XML 是否与给定的 XSD schema 匹配,并且与预期的 XML 响应相匹配。还可以使用 XPath 表达式来评估和比较响应 XML 中的各种值。

4.1、Endpoint 的依赖 {#41endpoint-的依赖}

在这个示例中,我们使用 @MockBean 来模拟 ProductEndpoint 所需的 Repository。如果没有这个模拟,Application Context 就无法启动,因为自动配置被禁用了。换句话说,测试框架不会在测试执行前配置任何 @Component@Service@Repository Bean。

不过,如果我们确实需要实际的组件依赖,而不是模拟,那么可以使用 @Import 声明这些组件。Spring 会查找这些类,然后根据需要将它们注入到端点中。

4.2、加载整个 Context {#42加载整个-context}

如前所述,@WebServiceServerTest 不会加载整个 Application Context。如果我们确实需要加载整个 Application Context 进行测试,那么我们应该考虑把 @SpringBootTest@AutoConfigureMockWebServiceClient 结合使用。然后,可以以类似的方式使用该客户端来发送请求并验证响应,如前所述。

5、总结 {#5总结}

本文介绍了 Spring Boot 中引入的 @WebServiceServerTest 注解,以及如何使用它为 Spring Boot 开发的 SOAP Web Service 构建快速、集中的集成测试。


参考:https://www.baeldung.com/spring-webserviceservertest

赞(2)
未经允许不得转载:工具盒子 » 使用 @WebServiceServerTest 进行 Spring Web Service 集成测试