java - 用于 Soap Web 服务的 JUnit 5 测试 - ParameterResolutionException:未注册 ParameterResolver
问题描述
我正在尝试使用 Java 1.8 为已发布的 .NET Soap Web 服务创建 JUnit 5 测试。
目前,我正在遵循代码库中 WebServiceClient 的模式,其中包含一个main()
方法。
WebServiceClient.java:
import static com.google.common.base.Preconditions.checkNotNull;
public class WebServiceClient {
IPersonWebService service;
public WebServiceClient(IPersonWebService service) {
this.service = checkNotNull(service);
}
private PersonData getPersonData() throws Exception {
return service.getPersons(null);
}
public static void main(String [] args) {
IPersonWebService service = new IPersonWebServiceImpl().getBasicHttpBindingIPersonWebServiceImpl();
WebServiceClient client = new WebServiceClient(service);
PersonData personData = client.getPersonData();
System.out.println(personData.toString());
}
}
需要遵循相同类型的功能:
WebServiceTest.java:
import org.junit.Before;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static com.google.common.base.Preconditions.checkNotNull;
public class WebServiceTest {
IPersonWebService service;
public WebServiceTest(IPersonWebService service) {
this.service = checkNotNull(service);
}
@Before
public void before(IPersonWebService service) {
this.service = checkNotNull(service);
}
@Test
public void testGetPersonData() throws Exception {
IPersonWebService service =
new IPersonWebServiceImpl().getBasicHttpBindingIPersonWebServiceImpl();
WebServiceTest client = new WebServiceTest(service);
PersonData personData = client.getPersonData();
assertThat(personData).isNotNull();
}
private PersonData getPersonData() throws Exception {
return service.getPersonData(null);
}
}
在 IntelliJ IDEA 中运行它会导致:
org.junit.jupiter.api.extension.ParameterResolutionException: No ParameterResolver registered for parameter [com.myapp.IPersonWebService arg0] in constructor [public com.myapp.WebServiceTest(com.myapp.IPersonWebService)].
at java.util.Optional.orElseGet(Optional.java:267)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at java.util.ArrayList.forEach(ArrayList.java:1249)
IPersonWebService.java:
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebService(name = "IPersonWebService", targetNamespace = "http://sample.org/")
@XmlSeeAlso({
ObjectFactory.class
})
public interface IPersonWebService {
@WebMethod(operationName = "GetPersonData",
action = "http://sample.org/IPersonWebService/GetPersonData")
@WebResult(name = "GetVehiclesResult",
targetNamespace = "http://sample.org/")
@RequestWrapper(localName = "GetPersonData",
targetNamespace = "http://sample.org/",
className = "com.myapp.GetPersonData")
@ResponseWrapper(localName = "GetPersonDataResponse",
targetNamespace = "http://sample.org/",
className = "com.myapp.GetPersonDataResponse")
public {PersonData} getPersonData(
@WebParam(name = "applicationID",
targetNamespace = "http://sample.org/")
String applicationID)
throws IPersonWebServicetExceptionFaultMessage;
}
这包含将被导入内存的实际 WSDL。
IPersonWebServiceImpl.java:
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.WebEndpoint;
import javax.xml.ws.WebServiceClient;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceFeature;
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.2.9-b130926.1035
* Generated source version: 2.2
*
*/
@WebServiceClient(name = "IPersonWebServiceImpl",
targetNamespace = "http://sample.org/",
wsdlLocation = "https://sample.com/WCF/IPersonWebServiceImpl.svc?singleWsdl")
public class IPersonWebServiceImpl
extends Service
{
private final static URL IPersonWebServiceImpl_WSDL_LOCATION;
private final static WebServiceException IPersonWebServiceImpl_EXCEPTION;
private final static QName IPersonWebServiceImpl_QNAME = new QName("http://sample.org/", "IPersonWebServiceImpl");
static {
URL url = null;
WebServiceException e = null;
try {
url = new URL("https://sample.com/WCF/IPersonWebServiceImpl.svc?singleWsdl");
} catch (MalformedURLException ex) {
e = new WebServiceException(ex);
}
IPersonWebServiceImpl_WSDL_LOCATION = url;
IPersonWebServiceImpl_EXCEPTION = e;
}
public IPersonWebServiceImpl() {
super(__getWsdlLocation(), IPersonWebServiceImpl_QNAME);
}
public IPersonWebServiceImpl(final WebServiceFeature... features) {
super(__getWsdlLocation(), IPersonWebServiceImpl_QNAME, features);
}
public IPersonWebServiceImpl(final URL wsdlLocation) {
super(wsdlLocation, IPersonWebServiceImpl_QNAME);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final WebServiceFeature... features) {
super(wsdlLocation, IPersonWebServiceImpl_QNAME, features);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final QName serviceName) {
super(wsdlLocation, serviceName);
}
public IPersonWebServiceImpl(final URL wsdlLocation,
final QName serviceName,
final WebServiceFeature... features) {
super(wsdlLocation, serviceName, features);
}
@WebEndpoint(name = "BasicHttpBinding_IPersonWebServiceImpl")
public IPersonWebServiceImpl getBasicHttpBindingIPersonWebServiceImpl() {
return super.getPort(new QName("http://sample.org/",
"BasicHttpBinding_IPersonWebServiceImpl"),
IPersonWebServiceImpl.class);
}
@WebEndpoint(name = "BasicHttpBinding_IPersonWebServiceImpl")
public IPersonWebServiceImpl getBasicHttpBindingIPersonWebServiceImpl(final WebServiceFeature... features) {
return super.getPort(new QName("http://sample.org/",
"BasicHttpBinding_IPersonWebServiceImpl"),
IPersonWebServiceImpl.class, features);
}
private static URL __getWsdlLocation() {
if (IPersonWebServiceImpl_EXCEPTION!= null) {
throw IPersonWebServiceImpl_EXCEPTION;
}
return IPersonWebServiceImpl_WSDL_LOCATION;
}
}
问题):
- 当尝试使用里面的模式
WebServiceClient.java
时,为什么会WebServiceTest.java
失败:
org.junit.jupiter.api.extension.ParameterResolutionException:
No ParameterResolver registered for parameter...
如何解决这个问题?
WebServiceClient.java
工作并显示从IPersonWebServiceImpl.java
(请注意这是如何在静态子句中显式设置 WSDL )获得的PersonData。
- 是否有第 3 方开源 Java 框架可用于导入任何类型的 WSDL(.NET 或其他)并使用 JUnit 5 测试基于 SOAP 的端点?
来自 RESTful Web 服务背景,而不是基于 SOAP 的背景,因此任何建议都将不胜感激。
解决方案
因此,首先,感谢您提供大量代码并明确您的问题。我知道你问已经几个月了,但我会尽力帮助的。
我认为有两个问题:异常(tldr 这是一个junit版本冲突),以及用于编写单元测试的结构化代码。
参数解析异常
@Test
从 JUnit5 导入,又名“junit-jupiter”,但@Before
来自 JUnit4,又名“junit-vintage”或“junit”。
import org.junit.Before; // junit4
import org.junit.jupiter.api.BeforeAll; // junit5
import org.junit.jupiter.api.Test; // junit5
因为@Test
来自 junit-jupiter,所以测试使用 JUnit5 运行,它(与 JUnit4 不同)允许在测试和构造函数方法中使用参数。WebServiceTest
的构造函数需要一个参数。但是您没有提供程序IPersonWebService
,因此 JUnit5 中断 - 它无法解决它。
所以要解决这个问题:
- 删除构造函数
- 从
before
方法中删除参数 - 更改
@Before
为@BeforeEach
(并修复导入)
我还建议(如果可能的话)排除您可能添加的任何 JUnit4 依赖项(junit-vintage
为了向后兼容),或者可能已经从其他依赖项中偷偷加入。它可以防止这样的混淆。
例如,使用 maven,要查看有哪些 JUnit 版本及其来源,查看和过滤依赖关系树:
mvn dependency:tree -Dincludes="*junit*"
编写单元测试
我不确定该应用程序的用途是什么,但是(正如我认为您已经发现的那样)它很难测试。这对我来说确实很奇怪。WebServiceClient
有一个 main 方法(我猜它会被直接调用?),它会立即创建 API 并调用它并返回结果——因此“业务逻辑”和“api”之间没有分离。也许那个主要方法是用来演示的?
最好把事情分开。希望它使测试更加清晰。
让我们做一个专门的WebServiceClient
. 这应该是唯一与 交互的类IPersonWebService
。
这里不应该有任何花哨的逻辑,只是带有基本异常处理的简单 API 调用。
// import generated soap stuff
public class WebServiceClient {
private final IPersonWebService service;
public WebServiceClient(IPersonWebService service) {
// an instance of the API is received in the constructor
this.service = service;
}
private PersonsData getPersonData() throws WebServiceClientException {
try {
// call the API
PersonsData persons = service.getPersonData(null);
if (personsData == null) {
throw new WebServiceClientException("Received null response from PersonsData :(");
}
return persons;
} catch (IPersonWebServicetExceptionFaultMessage e) {
// wrap the SOAP exception in our own wrapper
// it's best not to let SOAP code spread into our project.
throw new WebServiceClientException("Tried fetching PersonsData, but got exception from API", e);
}
}
}
这是该服务的测试类。
每种@Test
方法都完全独立于其他方法,它有自己的小气泡来保持测试独立。
我真的建议使用 Mockito 之类的模拟框架来创建虚拟实例。它真的很强大。在这种情况下,这意味着我们可以轻松地测试异常。Mockito 3与 JUnit5 配合得非常好——我在这里使用了参数注入来为测试制作模拟(Mockito 在后台提供了一个参数解析器)。
@ExtendWith(MockitoExtension.class)
class WebServiceClientTest {
@Test
public void testWebServiceClientCreated(
// it's not actually important about the internal workings of IPersonWebService
// so use mockito to mock it!
// all we care about is that if it's there, then WebServiceClient can be created
@Mock
IPersonWebService service) {
WebServiceClient wsc = new WebServiceClient(service);
assertNotNull(wsc);
}
// test happy flow
@Test
public void testPersonsDataReturned(
// again, we don't care about the internals of IPersonWebService, just that it returns data
// so mock it!
@Mock
IPersonWebService service,
// it's also not important about the internals of PersonsData,
// just that it's an object that's returned
@Mock
PersonsData personsDataMock) {
// set up the mock
when(service.getPersonsData(isNull())).thenReturn(personsDataMock);
WebServiceClient wsc = new WebServiceClient(service);
// we don't need to check WebServiceClient here! We already have a test that does this.
// each test should only focus on one thing
// assertNotNull(wsc);
PersonsData result = wsc.getPersonsData();
// now we can check the result
assertNotNull(result);
assertEquals(personsDataMock, result);
}
// again, testing for failure is much more interesting than happy flows
// in this case, the test will fail!
// we'd better fix WebServiceClient to handle unexpected exceptions from IPersonWebService
@Test
public void testWhenApiThrowsNullPointerExceptionExpectWebServiceClientException(
@Mock
IPersonWebService service) {
// mock throwing an exception
NullPointerException npe = new NullPointerException("Another one of those weird external webservice exceptions");
doThrow()
.when(service.getPersonsData(isNull()));
WebServiceClient wsc = new WebServiceClient(service);
WebServiceClientException thrownException = assertThrows(WebServiceClientException.class,
() -> wsc.getPersonsData()
);
// now we can check the result
assertNotNull(thrownException);
assertEquals("Tried fetching PersonsData, but got exception from API", thrownException.getMessage());
assertEquals(npe, thrownException.getCause());
}
}
并且是处理 API 时任何异常的一个很好的包装器。
class WebServiceClientException extends Exception {
public WebServiceClientException(String message) {
super(message);
}
public WebServiceClientException(String message, Exception cause) {
super(message, cause);
}
}
推荐阅读
- javascript - SVG 到画布到 .webm,而不是原始清洁
- javascript - 响应式视频以及 flex 中的溢出元素
- vb.net - 如何将字符串拆分为特定顺序的行
- c# - 优化在线面试评估的 PriorityQueue 实施
- java - 基于字段对用户定义的循环链表上的对象进行排序
- node.js - 在nodejs中通过fluent-ffmpeg合并多个视频文件和流
- c++ - 如何使用带有 WSL 的 g++ 增加堆栈大小?(适用于 Linux 的 Windows 子系统)
- websocket - 转发https连接:客户端A->代理服务器B->客户端C->服务器D
- python - 在包含列表值的字典中使用 for 循环和 if 条件
- html - 是否可以将焦点集中在父元素内而不被所有子元素应用但一个特定的?