Java 对 JAX-RS Web 服务进行单元测试?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/121266/
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
Unit testing a JAX-RS Web Service?
提问by Einar
I'm currently looking for ways to create automated tests for a JAX-RS(Java API for RESTful Web Services) based web service.
我目前正在寻找为基于JAX-RS(RESTful Web 服务的 Java API)的 Web 服务创建自动化测试的方法。
I basically need a way to send it certain inputs and verify that I get the expected responses. I'd prefer to do this via JUnit, but I'm not sure how that can be achieved.
我基本上需要一种方法来向它发送某些输入并验证我是否得到了预期的响应。我更喜欢通过 JUnit 来做到这一点,但我不确定如何实现。
What approach do you use to test your web-services?
您使用什么方法来测试您的网络服务?
Update:As entzik pointed out, decoupling the web service from the business logic allows me to unit test the business logic. However, I also want to test for the correct HTTP status codes etc.
更新:正如 entzik 所指出的,将 Web 服务与业务逻辑解耦允许我对业务逻辑进行单元测试。但是,我还想测试正确的 HTTP 状态代码等。
采纳答案by James Strachan
Jerseycomes with a great RESTful client API that makes writing unit tests really easy. See the unit tests in the examples that ship with Jersey. We use this approach to test the REST support in Apache Camel, if you are interested the test cases are here
Jersey带有一个很棒的 RESTful 客户端 API,可以让编写单元测试变得非常容易。请参阅 Jersey 附带的示例中的单元测试。我们使用这种方法来测试Apache Camel 中的 REST 支持,如果您有兴趣,测试用例在这里
回答by entzik
You probably wrote some java code that implements your business logic and then you have generated the web services end point for it.
您可能编写了一些实现业务逻辑的 Java 代码,然后为它生成了 Web 服务端点。
An important thing to do is to independently test your business logic. Since it's pure java code you can do that with regular JUnit tests.
要做的一件重要事情是独立测试您的业务逻辑。由于它是纯 Java 代码,因此您可以使用常规的 JUnit 测试来做到这一点。
Now, since the web services part is just an end point, what you want to make sure is that the generated plumbing (stubs, etc) are in sync with your java code. you can do that by writing JUnit tests that invoke the generated web service java clients. This will let you know when you change your java signatures without updating the web services stuff.
现在,由于 Web 服务部分只是一个端点,您要确保生成的管道(存根等)与您的 Java 代码同步。您可以通过编写调用生成的 Web 服务 java 客户端的 JUnit 测试来做到这一点。这将让您知道何时更改 Java 签名而不更新 Web 服务内容。
If your web services plumbing is automatically generated by your build system at every build, then it may not be necessary to test the end points (assuming it's all properly generated). Depends on your level of paranoia.
如果您的 Web 服务管道是由您的构建系统在每次构建时自动生成的,那么可能没有必要测试端点(假设它全部正确生成)。取决于你的偏执程度。
回答by Chris Dail
I use Apache's HTTPClient (http://hc.apache.org/)to call Restful Services. The HTTP Client library allows you to easily perform get, post or whatever other operation you need. If your service uses JAXB for xml binding, you can create a JAXBContext to serialize and deserialize inputs and outputs from the HTTP request.
我使用 Apache 的HTTPClient (http://hc.apache.org/)来调用 Restful 服务。HTTP 客户端库允许您轻松执行获取、发布或您需要的任何其他操作。如果您的服务使用 JAXB 进行 xml 绑定,您可以创建一个 JAXBContext 来序列化和反序列化来自 HTTP 请求的输入和输出。
回答by James Strachan
Though its too late from the date of posting the question, thought this might be useful for others who have a similar question. Jersey comes with a test framework called the Jersey Test Frameworkwhich allows you to test your RESTful Web Service, including the response status codes. You can use it to run your tests on lightweight containers like Grizzly, HTTPServer and/or EmbeddedGlassFish. Also, the framework could be used to run your tests on a regular web container like GlassFish or Tomcat.
尽管从发布问题之日起为时已晚,但认为这可能对有类似问题的其他人有用。Jersey 附带了一个名为Jersey 测试框架的测试框架,它允许您测试 RESTful Web 服务,包括响应状态代码。您可以使用它在轻量级容器(如 Grizzly、HTTPServer 和/或 EmbeddedGlassFish)上运行测试。此外,该框架还可用于在常规 Web 容器(如 GlassFish 或 Tomcat)上运行您的测试。
回答by James Strachan
An important thing to do is to independently test your business logic
一个重要的事情是独立测试你的业务逻辑
I certainly would not assume that the person who wrote the JAX-RS code and is looking to unit test the interface is somehow, for some bizarre, inexplicable reason, oblivious to the notion that he or she can unit testing other parts of the program, including business logic classes. It's hardly helpful to state the obvious and the point was repeatedly made that the responses need to be tested, too.
我当然不会假设编写 JAX-RS 代码并希望对接口进行单元测试的人不知何故,出于某种奇怪的、莫名其妙的原因,不知道他或她可以对程序的其他部分进行单元测试,包括业务逻辑类。陈述显而易见的事情几乎没有帮助,而且人们反复强调响应也需要测试。
Both Jersey and RESTEasy have client applications and in the case of RESTEasy you can use the same annoations (even factor out annotated interface and use on the client and server side of your tests).
Jersey 和 RESTEasy 都有客户端应用程序,在 RESTEasy 的情况下,您可以使用相同的注解(甚至排除带注释的接口并在测试的客户端和服务器端使用)。
REST not what this service can do for you; REST what you can do for this service.
REST 不是这项服务能为您做什么;REST 你可以为这个服务做什么。
回答by Johan
You can try out REST Assuredwhich makes it verysimple to test REST services and validating the response in Java (using JUnit or TestNG).
您可以试用REST Assured,这使得测试 REST 服务和验证 Java 响应变得非常简单(使用 JUnit 或 TestNG)。
回答by F?rat Kü?üK
As James said; There is built-in test frameworkfor Jersey. A simple hello world example can be like this:
正如詹姆斯所说;Jersey有内置的测试框架。一个简单的 hello world 示例可以是这样的:
pom.xml for maven integration. When you run mvn test
. Frameworks start a grizzly container. You can use jetty or tomcat via changing dependencies.
用于 maven 集成的 pom.xml。当你运行mvn test
. 框架启动了一个灰熊容器。您可以通过更改依赖项来使用 jetty 或 tomcat。
...
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.16</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.16</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.16</version>
<scope>test</scope>
</dependency>
</dependencies>
...
ExampleApp.java
示例应用程序.java
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/")
public class ExampleApp extends Application {
}
HelloWorld.java
你好世界
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Path("/")
public final class HelloWorld {
@GET
@Path("/hello")
@Produces(MediaType.TEXT_PLAIN)
public String sayHelloWorld() {
return "Hello World!";
}
}
HelloWorldTest.java
HelloWorldTest.java
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import javax.ws.rs.core.Application;
import static org.junit.Assert.assertEquals;
public class HelloWorldTest extends JerseyTest {
@Test
public void testSayHello() {
final String hello = target("hello").request().get(String.class);
assertEquals("Hello World!", hello);
}
@Override
protected Application configure() {
return new ResourceConfig(HelloWorld.class);
}
}
You can check thissample application.
您可以查看此示例应用程序。
回答by Ashish Shinde
Take a look at Alchemy rest client generator. This can generate a proxy implementation for your JAX-RS webservice class using jersey client behind the scene. Effectively you will call you webservice methods as simple java methods from your unit tests. Handles http authentication as well.
看看Alchemy 休息客户端生成器。这可以在幕后使用 jersey 客户端为您的 JAX-RS web 服务类生成代理实现。实际上,您将从单元测试中将 Web 服务方法称为简单的 java 方法。也处理 http 身份验证。
There is no code generation involved if you need to simply run tests so it is convenient.
如果您需要简单地运行测试,则不涉及代码生成,因此很方便。
Dislclaimer: I am the author of this library.
免责声明:我是这个库的作者。
回答by Alexandr
As I understand the main purpose of the auther of this issue is to decouple JAX RS layer from business one. And unit test only the first one. Two basic problems here we have to resolve:
据我了解,此问题作者的主要目的是将 JAX RS 层与业务层分离。并且单元测试只有第一个。这里我们必须解决两个基本问题:
- Run in test some web/application server, put JAX RS components in it. And only them.
- Mock business services inside JAX RS components/REST layer.
- 在测试中运行一些 Web/应用程序服务器,将 JAX RS 组件放入其中。而只有他们。
- 模拟 JAX RS 组件/REST 层内的业务服务。
The first one is solved with Arquillian. The second one is perfectly described in arquillican and mock
第一个是用 Arquillian 解决的。第二个在arquillican 和 mock 中得到了完美的描述
Here is an example of the code, it may differ if you use another application server, but I hope you'll get the basic idea and advantages.
这是代码示例,如果您使用其他应用程序服务器可能会有所不同,但我希望您能了解基本思想和优点。
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import com.brandmaker.skinning.service.SomeBean;
/**
* Created by alexandr on 31.07.15.
*/
@Path("/entities")
public class RestBean
{
@Inject
SomeBean bean;
@GET
public String getEntiry()
{
return bean.methodToBeMoked();
}
}
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import com.google.common.collect.Sets;
/**
*/
@ApplicationPath("res")
public class JAXRSConfiguration extends Application
{
@Override
public Set<Class<?>> getClasses()
{
return Sets.newHashSet(RestBean.class);
}
}
public class SomeBean
{
public String methodToBeMoked()
{
return "Original";
}
}
import javax.enterprise.inject.Specializes;
import com.brandmaker.skinning.service.SomeBean;
/**
*/
@Specializes
public class SomeBeanMock extends SomeBean
{
@Override
public String methodToBeMoked()
{
return "Mocked";
}
}
@RunWith(Arquillian.class)
public class RestBeanTest
{
@Deployment
public static WebArchive createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive.class, "test.war")
.addClasses(JAXRSConfiguration.class, RestBean.class, SomeBean.class, SomeBeanMock.class)
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
System.out.println(war.toString(true));
return war;
}
@Test
public void should_create_greeting() {
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://127.0.0.1:8181/test/res/entities");
//Building the request i.e a GET request to the RESTful Webservice defined
//by the URI in the WebTarget instance.
Invocation invocation = target.request().buildGet();
//Invoking the request to the RESTful API and capturing the Response.
Response response = invocation.invoke();
//As we know that this RESTful Webserivce returns the XML data which can be unmarshalled
//into the instance of Books by using JAXB.
Assert.assertEquals("Mocked", response.readEntity(String.class));
}
}
A couple of notes:
一些注意事项:
- JAX RS configuration without web.xml is used here.
- JAX RS Client is used here (no RESTEasy/Jersey, they expose more convenient API)
- When test starts, Arquillian's runner starts working. Hereyou can find how to configure tests for Arquillian with needed application server.
- Depending on the chosen application server, an url in the test will differ a little bit. Another port may be used. 8181 is used by Glassfish Embedded in my example.
- 此处使用不带 web.xml 的 JAX RS 配置。
- 这里使用的是 JAX RS Client(没有 RESTEasy/Jersey,它们暴露了更方便的 API)
- 当测试开始时,Arquillian 的运行程序开始工作。在这里您可以找到如何使用所需的应用程序服务器为 Arquillian 配置测试。
- 根据所选的应用程序服务器,测试中的 url 会略有不同。可以使用另一个端口。Glassfish Embedded 在我的示例中使用了 8181。
Hope, it'll help.
希望,它会有所帮助。
回答by keyoxy
Keep it simple. Have a look at https://github.com/valid4j/http-matcherswhich can be imported from Maven Central.
把事情简单化。查看可以从 Maven Central 导入的https://github.com/valid4j/http-matchers。
<dependency>
<groupId>org.valid4j</groupId>
<artifactId>http-matchers</artifactId>
<version>1.0</version>
</dependency>
Usage example:
用法示例:
// Statically import the library entry point:
import static org.valid4j.matchers.http.HttpResponseMatchers.*;
// Invoke your web service using plain JAX-RS. E.g:
Client client = ClientBuilder.newClient();
Response response = client.target("http://example.org/hello").request("text/plain").get();
// Verify the response
assertThat(response, hasStatus(Status.OK));
assertThat(response, hasHeader("Content-Encoding", equalTo("gzip")));
assertThat(response, hasEntity(equalTo("content")));
// etc...