如何在Cucumber测试中通过运行时导入WSDL实现动态SOAP客户端?
我懂你要的是什么——不想提前用wsimport生成一堆静态代码,而是在Cucumber测试运行时,直接拉取远程服务器的WSDL,动态生成SOAP客户端对吧?这完全可以实现,下面给你两种实用方案,都是Java生态下的,完美适配Cucumber的测试场景:
方案1:使用JAX-WS原生动态客户端(无需额外依赖)
JDK自带的JAX-WS API就支持动态解析WSDL,不需要引入第三方库,核心是用Service和Dispatch类来实现。
方式A:基于Dispatch的完全动态调用(无需预定义接口)
这种方式不需要任何提前生成的代码,直接构建SOAP请求并解析响应,灵活性拉满:
import javax.xml.ws.Dispatch; import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; import javax.xml.namespace.QName; import java.net.URL; import org.w3c.dom.Element; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; public class SoapDynamicClient { public Dispatch<Element> createDispatch(String wsdlUrl, String serviceNs, String serviceName, String portName) throws Exception { URL wsdlLocation = new URL(wsdlUrl); // 从WSDL中获取服务的QName(命名空间+服务名) QName serviceQName = new QName(serviceNs, serviceName); Service service = Service.create(wsdlLocation, serviceQName); // 获取端口的QName QName portQName = new QName(serviceNs, portName); // 创建处理Payload的Dispatch实例 Dispatch<Element> dispatch = service.createDispatch(portQName, Element.class, Service.Mode.PAYLOAD); // 可选:设置SOAP版本(比如SOAP 1.1) ((SOAPBinding) dispatch.getBinding()).setSOAPVersion(SOAPBinding.SOAP11HTTP_BINDING); return dispatch; } }
在Cucumber步骤中调用的示例:
import io.cucumber.java.en.Given; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.equalTo; public class SoapTestSteps { private Dispatch<Element> dispatch; @Given("初始化SOAP动态客户端,WSDL地址为{string}") public void initClient(String wsdlUrl) throws Exception { SoapDynamicClient clientHelper = new SoapDynamicClient(); dispatch = clientHelper.createDispatch( wsdlUrl, "http://your-service-namespace.com/", // 从WSDL的targetNamespace获取 "YourServiceName", // WSDL中<service>标签的name属性 "YourServicePort" // WSDL中<port>标签的name属性 ); } @Given("调用服务的{string}方法,参数为{string}") public void callServiceMethod(String methodName, String paramValue) throws Exception { // 手动构建SOAP请求的Payload DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); Document doc = dbf.newDocumentBuilder().newDocument(); Element methodElement = doc.createElementNS("http://your-service-namespace.com/", methodName); Element paramElement = doc.createElement("paramName"); // 对应WSDL中的参数名 paramElement.setTextContent(paramValue); methodElement.appendChild(paramElement); // 发送请求并获取响应 Element response = dispatch.invoke(methodElement); // 断言响应结果 assertThat(response.getTextContent(), equalTo("预期返回值")); } }
方案2:使用Apache CXF动态客户端(更简洁,推荐)
如果觉得手动构建DOM太麻烦,Apache CXF提供了更易用的动态客户端API,能自动解析WSDL并生成动态代理,直接通过方法名调用服务。
第一步:添加CXF依赖(Maven为例)
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.6.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.6.2</version> <scope>test</scope> </dependency>
第二步:编写动态客户端工具类
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; import org.apache.cxf.endpoint.Client; public class CxfDynamicClient { public Client createClient(String wsdlUrl) throws Exception { JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance(); // 直接加载WSDL生成客户端 return factory.createClient(wsdlUrl); } }
在Cucumber步骤中使用
import io.cucumber.java.en.Given; import io.cucumber.java.Before; import org.apache.cxf.endpoint.Client; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.equalTo; public class CxfSoapTestSteps { private Client cxfClient; // 提前初始化客户端,避免每次步骤重复加载WSDL @Before public void setUp() throws Exception { CxfDynamicClient clientHelper = new CxfDynamicClient(); cxfClient = clientHelper.createClient("http://remote-server/your-service?wsdl"); } @Given("调用CXF动态客户端的{string}方法,参数为{string}") public void callCxfService(String methodName, String param) throws Exception { // 直接通过方法名调用,参数按顺序传入 Object[] responses = cxfClient.invoke(methodName, param); // 处理响应结果(responses数组对应方法的返回值/输出参数) assertThat(responses[0], equalTo("预期返回值")); } }
额外注意事项
- 认证处理:如果WSDL或服务需要Basic Auth,CXF可以添加拦截器:
JAX-WS原生Dispatch可以通过cxfClient.getOutInterceptors().add(new org.apache.cxf.interceptor.security.AuthenticationInterceptor("username", "password"));BindingProvider设置:BindingProvider bp = (BindingProvider) dispatch; Map<String, Object> context = bp.getRequestContext(); context.put(BindingProvider.USERNAME_PROPERTY, "username"); context.put(BindingProvider.PASSWORD_PROPERTY, "password"); - 性能优化:在Cucumber的
@Before钩子中初始化客户端,复用实例,避免每次步骤都重新加载WSDL。
内容的提问来源于stack exchange,提问作者user35253
相关产品推荐
相关产品推荐

