java 如何对调用 Jersey 客户端 API 的代码进行单元测试?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11005279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I unit test code which calls the Jersey Client API?
提问by BennyMcBenBen
I wrote code which calls the Jersey client API which in turn calls a web service which is out of my control. I do not want my unit test to call the actual web service.
我编写了调用 Jersey 客户端 API 的代码,而后者又调用了我无法控制的 Web 服务。我不希望我的单元测试调用实际的 Web 服务。
What is the best approach for writing a unit test for code which calls the Jersey client API? Should I use the Jersey server API to write a JAX-RS web service and then use the Jersey Test Framework for the unit test? Or should I mock out the Jersey web service calls? I have access to JMock. Or should I try another approach?
为调用 Jersey 客户端 API 的代码编写单元测试的最佳方法是什么?我应该使用 Jersey 服务器 API 编写 JAX-RS Web 服务,然后使用 Jersey 测试框架进行单元测试吗?或者我应该模拟 Jersey 网络服务调用?我可以访问 JMock。或者我应该尝试另一种方法?
During my research, I found this discussiondescribing various options, but I did find a complete solution. Are there any code examples available showing a suggested JUnit approach? I could not find any in the Jersey documentation.
在我的研究过程中,我发现这个讨论描述了各种选项,但我确实找到了一个完整的解决方案。是否有任何代码示例显示建议的 JUnit 方法?我在泽西文档中找不到任何内容。
Here is the relevant source code:
这是相关的源代码:
public String getResult(URI uri) throws Exception {
// error handling code removed for clarity
ClientConfig clientConfig = new DefaultClientConfig();
Client client = Client.create(clientConfig);
WebResource service = client.resource(uri);
String result = service.accept(accept).get(String.class);
return result;
}
Here are examples of test code I would like to pass. I would like to test (1) passing in a valid URI and getting a valid string back and (2) passing in an invalid (for whatever reason -- unreachable or unauthorized) URI and getting an exception back.
以下是我想通过的测试代码示例。我想测试 (1) 传入一个有效的 URI 并返回一个有效的字符串,以及 (2) 传入一个无效的(无论出于何种原因——无法访问或未经授权的)URI 并返回一个异常。
@Test
public void testGetResult_ValidUri() throws Exception {
String xml = retriever.getResult(VALID_URI);
Assert.assertFalse(StringUtils.isBlank(xml));
}
@Test(expected = IllegalArgumentException.class)
public void testGetResult_InvalidUri() throws Exception {
retriever.getResult(INVALID_URI);
}
Everything above is the simple description of what my code does. In reality, there is a layer on top of that that accepts two URIs, first tries calling the first URI, and if that URI fails then it tries calling the second URI. I would like to have unit tests covering (1) the first URI succeeds, (2) the first URI fails and the second URI succeeds, and (3) both URIs fail. This code is sufficiently complex that I want to test these different scenarios using JUnit, but to do this I either need to run actual stand-in web services or mock out the Jersey client API calls.
上面的所有内容都是对我的代码功能的简单描述。实际上,在它之上有一层接受两个 URI,首先尝试调用第一个 URI,如果该 URI 失败,则它会尝试调用第二个 URI。我希望单元测试涵盖 (1) 第一个 URI 成功,(2) 第一个 URI 失败,第二个 URI 成功,以及 (3) 两个 URI 都失败。这段代码非常复杂,我想使用 JUnit 测试这些不同的场景,但要做到这一点,我需要运行实际的替代 Web 服务或模拟 Jersey 客户端 API 调用。
采纳答案by Piotr Kochański
Try to use Mockito or Easymock for mocking service calls. You need to mock only these methods which are actually used - no need to mock every method. You can creat mock object for WebResource class, then mock accept method call.
尝试使用 Mockito 或 Easymock 来模拟服务调用。您只需要模拟实际使用的这些方法 - 无需模拟每个方法。您可以为 WebResource 类创建模拟对象,然后模拟接受方法调用。
In @BeforeClass/@Before JUnit test method write something like (Mockito example)
在@BeforeClass/@Before JUnit 测试方法中编写类似(Mockito 示例)
WebResource res = mock(WebResource.class);
when(res.accept(something)).thenReturn(thatWhatYouWant);
Then in your tests you can use res object as if it was real object and call mock method on it. Instead of returning value you can also throw exceptions. Mockitois pretty cool.
然后在您的测试中,您可以像使用真实对象一样使用 res 对象并在其上调用模拟方法。除了返回值,您还可以抛出异常。Mockito很酷。
回答by vertti
Typically what you are really after is "does the way I use the Jersey Client DSL produce a request to the correct URL with the correct payload and URL parameters". Testing this with Mockito is really verbose and the setup code will usually end up looking something like this:
通常,您真正想要的是“我使用 Jersey 客户端 DSL 的方式是否会使用正确的有效负载和 URL 参数生成对正确 URL 的请求”。用 Mockito 测试这个真的很冗长,设置代码通常最终看起来像这样:
when(authentication.queryParam(eq("sa"), anyBoolean())).thenReturn(testAuthentication);
when(testAuthentication.resolveTemplate("channel", "smf")).thenReturn(testAuthentication);
when(testAuthentication.request(
MediaType.APPLICATION_JSON_TYPE)).thenReturn(mockRequestBuilder);
when(mockRequestBuilder.post(any(Entity.class))).thenReturn(mockResponse);
when(mockResponse.readEntity(ResponseWrapper.class)).thenReturn(successfulAuthResponse());
And this is basically just for a single REST request. It's overly verbose, and instead of testing the hoped outcome you are just replicating the steps you think are correct in using the Jersey Client DSL.
这基本上仅适用于单个 REST 请求。它过于冗长,您只是复制您认为在使用 Jersey 客户端 DSL 时正确的步骤,而不是测试预期的结果。
Instead of the above, I would aim for mocking a simple service. For this I've used WireMockwhich starts a Jetty server and where I can stub things like "expect a request to this URL, respond with this message and verify that the payload is this".
与上述不同,我的目标是模拟一个简单的服务。为此,我使用了WireMock,它启动了一个 Jetty 服务器,我可以在其中存根诸如“期望对这个 URL 的请求,用这个消息响应并验证有效负载是这个”之类的东西。
I know this is edging on an integration test and it is a bit slower than just using Mockito but I value testing the real outcome and I value the readability of the tests way more in this case.
我知道这是集成测试的边缘,它比仅使用 Mockito 慢一点,但我重视测试真实结果,并且在这种情况下我更重视测试的可读性。
Setup for a WireMock based Jersey Client test looks something like this:
基于 WireMock 的 Jersey 客户端测试的设置如下所示:
@Test
public void exactUrlOnly() {
stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
assertThat(testClient.get("/some/thing").statusCode(), is(200));
assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
回答by Chase
Just implement a work-alike service and in your unit test setup start the service using HttpServerFactory.
只需实现一个类似工作的服务,并在您的单元测试设置中使用 HttpServerFactory 启动该服务。