代码之家  ›  专栏  ›  技术社区  ›  Einar

单元测试JAX-RS Web服务?

  •  75
  • Einar  · 技术社区  · 16 年前

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

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

    您使用什么方法来测试Web服务?

    更新: 正如Entzik指出的,将Web服务与业务逻辑分离允许我对业务逻辑进行单元测试。但是,我还想测试正确的HTTP状态代码等。

    10 回复  |  直到 7 年前
        1
  •  35
  •   James Strachan    16 年前

    Jersey 附带了一个非常好的RESTful客户机API,使编写单元测试变得非常容易。请参见与Jersey一起提供的示例中的单元测试。我们使用这种方法来测试 Apache Camel ,如果您感兴趣 test cases are here

        2
  •  23
  •   Johan    14 年前

    你可以试一试 REST Assured 这使得它 非常 简单地测试REST服务并在Java中验证响应(使用JUnit或TestNG)。

        3
  •  10
  •   Yasin Okumuş    7 年前

    正如詹姆斯所说;有内置的 test framework 为了Jersey。一个简单的Hello World示例如下:

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

    JAVA

    import javax.ws.rs.ApplicationPath;
    import javax.ws.rs.core.Application;
    
    @ApplicationPath("/")
    public class ExampleApp extends Application {
    
    }
    

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

    你可以检查 this 示例应用程序。

        4
  •  6
  •   Bill the Lizard Hacknightly    12 年前

    虽然从发布问题的日期算起已经太晚了,但认为这可能对其他有类似问题的人有用。 泽西岛有一个称为 Jersey Test Framework 它允许您测试RESTful Web服务,包括响应状态代码。您可以使用它在诸如grizzly、httpserver和/或embeddedglassfish等轻型容器上运行测试。此外,该框架还可以用于在常规Web容器(如GlassFish或Tomcat)上运行测试。

        5
  •  5
  •   entzik    16 年前

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

    要做的一件重要的事情是独立地测试业务逻辑。因为它是纯Java代码,所以可以用常规的JUnit测试来实现。

    现在,由于Web服务部分只是一个端点,所以您要确保的是生成的管道(存根等)与Java代码同步。可以通过编写调用生成的Web服务Java客户端的JUnit测试来实现这一点。这将使您知道在不更新Web服务的情况下更改Java签名时的情况。

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

        6
  •  3
  •   Chris Dail    16 年前

    我使用Apache的 HTTPClient (http://hc.apache.org/) 调用RESTful服务。HTTP客户机库允许您轻松地执行GET、POST或任何其他需要的操作。如果您的服务使用JAXB进行XML绑定,那么您可以创建一个JAXBContext来序列化和反序列化来自HTTP请求的输入和输出。

        7
  •  3
  •   Ashish Shinde    9 年前

    看一看 Alchemy rest client generator . 这可以使用场景后面的Jersey客户端为JAX-RS WebService类生成代理实现。实际上,您将调用WebService方法作为单元测试中的简单Java方法。也处理HTTP身份验证。

    如果您需要简单地运行测试,就不需要生成代码,因此这很方便。

    迪斯克莱默:我是这个图书馆的作者。

        8
  •  2
  •   rickoshay    15 年前

    要做的一件重要的事情是独立地测试业务逻辑

    我当然不会假定编写JAX-RS代码并希望对接口进行单元测试的人不知何故,出于某种奇怪的、无法解释的原因,忘记了他或她可以对程序的其他部分(包括业务逻辑类)进行单元测试的概念。陈述显而易见的事实并没有什么帮助,并且反复指出,这些反应也需要测试。

    Jersey和Resteay都有客户机应用程序,在Resteay的情况下,您可以使用相同的注释(甚至考虑注释的接口,并在测试的客户机和服务器端使用)。

    不要为这项服务所能做的事而休息;要为这项服务所能做的事而休息。

        9
  •  2
  •   keyoxy    8 年前

    保持简单。看一看 https://github.com/valid4j/http-matchers 可以从马文中心进口。

        <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...
    
        10
  •  1
  •   Alexandr    9 年前

    据我所知,这个问题的作者的主要目的是将JaxRS层与BusinessOne分离开来。只有第一个单元测试。我们必须解决两个基本问题:

    1. 运行测试一些Web/应用服务器,将JAX RS组件放入 它。只有他们。
    2. JAX RS内部模拟业务服务 组件/休息层。

    第一个问题用阿奎利安语解决。 第二个在 arquillican and 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. 当测试开始时,阿奎利安的跑步者开始工作。 Here 您可以找到如何使用所需的应用服务器为arquillian配置测试。
    4. 根据所选的应用程序服务器,在 测试会有点不同。可以使用另一个端口。8181是 嵌入在我的示例中的玻璃鱼使用。

    希望,这会有所帮助。