测试 JAX-RS Web 服务?

发布于 2024-07-06 03:18:15 字数 328 浏览 10 评论 0原文

我目前正在寻找为 JAX-RS (用于 RESTful Web 的 Java API服务)基于网络服务。

我基本上需要一种方法来向其发送某些输入并验证我是否得到了预期的响应。 我更愿意通过 JUnit 来完成此操作,但我不确定如何实现。

您使用什么方法来测试您的网络服务?

更新: 正如 entzik 指出的,将 Web 服务与业务逻辑解耦使我能够对业务逻辑进行单元测试。 但是,我还想测试正确的 HTTP 状态代码等。

I'm currently looking for ways to create automated tests for a JAX-RS (Java API for RESTful Web Services) based web service.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

冰火雁神 2024-07-13 03:18:16

您可以尝试REST Assured,这会让事情变得非常简单测试 REST 服务并验证 Java 中的响应(使用 JUnit 或 TestNG)。

You can try out REST Assured which makes it very simple to test REST services and validating the response in Java (using JUnit or TestNG).

等风来 2024-07-13 03:18:16

正如詹姆斯所说; Jersey 有内置的 测试框架。 一个简单的 hello world 示例可以是这样的:

用于 maven 集成的 pom.xml。 当您运行mvn test时。 框架启动一个 grizzly 容器。 您可以通过更改依赖项来使用 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

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

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

您可以查看示例应用程序。

As James said; There is built-in test framework for Jersey. A simple hello world example can be like this:

pom.xml for maven integration. When you run mvn test. Frameworks start a grizzly container. You can use jetty or tomcat via changing dependencies.

...
<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

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

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 this sample application.

萌酱 2024-07-13 03:18:16

您可能编写了一些实现业务逻辑的 Java 代码,然后为其生成了 Web 服务端点。

要做的一件重要的事情是独立测试您的业务逻辑。 由于它是纯 java 代码,因此您可以通过常规 J​​Unit 测试来做到这一点。

现在,由于 Web 服务部分只是一个端点,因此您需要确保生成的管道(存根等)与您的 java 代码同步。 您可以通过编写调用生成的 Web 服务 Java 客户端的 JUnit 测试来实现这一点。 当您更改 Java 签名而不更新 Web 服务内容时,这会让您知道。

如果您的 Web 服务管道是由您的构建系统在每次构建时自动生成的,那么可能不需要测试端点(假设它全部正确生成)。 取决于你的偏执程度。

You probably wrote some java code that implements your business logic and then you have generated the web services end point for it.

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.

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.

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.

挽袖吟 2024-07-13 03:18:16

虽然从发布问题之日起为时已晚,但我认为这对于有类似问题的其他人可能有用。
Jersey 附带了一个名为 Jersey 测试框架 的测试框架,它允许您测试 RESTful Web 服务,包括响应状态代码。 您可以使用它在 Grizzly、HTTPServer 和/或 EmbeddedGlassFish 等轻量级容器上运行测试。 此外,该框架还可用于在常规 Web 容器(如 GlassFish 或 Tomcat)上运行测试。

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 Framework which 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.

醉酒的小男人 2024-07-13 03:18:16

查看 Alchemy Rest 客户端生成器。 这可以在后台使用 jersey 客户端为您的 JAX-RS Web 服务类生成代理实现。 实际上,您将在单元测试中将 Web 服务方法称为简单的 Java 方法。 还处理 http 身份验证。

如果您只需要运行测试,则不涉及代码生成,因此很方便。

免责声明:我是这个库的作者。

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.

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.

时光清浅 2024-07-13 03:18:16

我使用 Apache 的 HTTPClient (http://hc.apache.org/) 来调用 Restful 服务。 HTTP 客户端库允许您轻松执行获取、发布或您需要的任何其他操作。 如果您的服务使用 JAXB 进行 xml 绑定,您可以创建 JAXBContext 来序列化和反序列化 HTTP 请求的输入和输出。

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.

我们的影子 2024-07-13 03:18:16

把事情简单化。 查看可以导入的 https://github.com/valid4j/http-matchers来自 Maven 中心。

    <dependency>
        <groupId>org.valid4j</groupId>
        <artifactId>http-matchers</artifactId>
        <version>1.0</version>
    </dependency>

使用示例:

// 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...

Keep it simple. Have a look at https://github.com/valid4j/http-matchers which can be imported from Maven Central.

    <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...
萌无敌 2024-07-13 03:18:16

<块引用>

要做的重要事情是独立测试您的业务逻辑

我当然不会认为编写 JAX-RS 代码并希望对接口进行单元测试的人不知何故,出于某种奇怪的、无法解释的原因,忽视了这样的概念:他或她可以对程序的其他部分进行单元测试,包括业务逻辑类。 陈述显而易见的事实几乎没有帮助,而且人们反复强调,响应也需要进行测试。

Jersey 和 RESTEasy 都有客户端应用程序,对于 RESTEasy,您可以使用相同的注释(甚至可以提取注释接口并在测试的客户端和服务器端使用)。

REST 不是该服务能为您做什么; 休息你可以为此服务做的事情。

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.

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).

REST not what this service can do for you; REST what you can do for this service.

画骨成沙 2024-07-13 03:18:16

据我了解,此问题作者的主要目的是将 JAX RS 层与业务层解耦。 并且仅对第一个进行单元测试。 这里我们必须解决两个基本问题:

  1. 运行测试一些 Web/应用程序服务器,将 JAX RS 组件放入
    它。 并且只有他们。
  2. JAX RS 中的模拟业务服务
    组件/REST 层。

第一个问题是用 Arquillian 解决的。
第二个在arquillian和mock中有完美的描述,

这里是代码示例,如果您使用其他应用程序服务器,它可能会有所不同,但我希望您能够了解基本概念和优点。

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

一些注意事项:

  1. 此处使用不带 web.xml 的 JAX RS 配置。
  2. 这里使用 JAX RS 客户端(没有 RESTEasy/Jersey,它们公开了更方便的 API)
  3. 当测试开始时,Arquillian 的运行器开始工作。 在这里您可以找到如何使用所需的应用程序服务器为 Arquillian 配置测试。
  4. 根据所选的应用程序服务器,
    测试会略有不同。 可以使用另一个端口。 8181 是
    在我的示例中由 Glassfish Embedded 使用。

希望,它会有所帮助。

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:

  1. Run in test some web/application server, put JAX RS components in
    it. And only them.
  2. Mock business services inside JAX RS
    components/REST layer.

The first one is solved with Arquillian.
The second one is perfectly described in arquillican and 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:

  1. JAX RS configuration without web.xml is used here.
  2. JAX RS Client is used here (no RESTEasy/Jersey, they expose more convenient API)
  3. When test starts, Arquillian's runner starts working. Here you can find how to configure tests for Arquillian with needed application server.
  4. 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.

Hope, it'll help.

雨轻弹 2024-07-13 03:18:15

Jersey 附带了一个很棒的 RESTful 客户端 API,使编写单元测试变得非常容易。 请参阅 Jersey 附带的示例中的单元测试。 我们使用这种方法来测试 Apache Camel 中的 REST 支持,如果您对测试用例在这里

Jersey comes 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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文