【问题标题】:Testing a JAX-RS Web Service?测试 JAX-RS Web 服务?
【发布时间】:2023-03-10 14:25:01
【问题描述】:

我目前正在寻找为基于JAX-RS(RESTful Web 服务的Java API)的Web 服务创建自动化测试的方法。

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

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

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

【问题讨论】:

  • 好问题 - 但是我想说,如果您正在通过 HTTP 进行测试,那么我会觉得这是集成测试。
  • 汤姆。你是绝对正确的。我们应该为此注入一个虚拟的 HTTP 模拟器/轻量级容器。在 node.js 世界中,supertest 做到了这一点。你可以模拟 express.js。

标签: java web-services unit-testing junit jax-rs


【解决方案1】:

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

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

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

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

【讨论】:

  • 你说的很对,虽然我还需要测试返回的实际 HTTP 响应,尤其是 HTTP 状态代码。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    【解决方案4】:

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

    【讨论】:

    • 你有一个关于如何模拟呼叫处理程序的好例子吗?JerseyHttpCall -> MyResource -> CallHandler.getSomething() 我们如何在这里模拟 CallHandler ?
    【解决方案5】:

    一个重要的事情是独立测试你的业务逻辑

    我当然不会假设编写 JAX-RS 代码并希望对接口进行单元测试的人不知何故,出于某种奇怪、莫名其妙的原因,忘记了他或她可以对其他部分进行单元测试的想法。程序,包括业务逻辑类。陈述显而易见的事实几乎没有帮助,并且反复强调了响应也需要进行测试。

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

    REST 不是该服务可以为您做什么; REST 您可以为此服务做什么。

    【讨论】:

    • 人们可能想要测试一些横切关注点。例如验证、身份验证、所需的 HTTP 标头等。因此人们可以更喜欢测试他们的 JAX-RS 代码。
    • 在我的应用程序中,我使用 ModelMapper 将“DTO”类“映射”到“业务对象”类,底层“服务”类可以理解这些类。这是一个很好的独立测试示例。
    • 有时 REST 小程序的复杂性如此之低,以至于模拟程序会比应用程序层大,就像我目前的情况一样。 :)
    【解决方案6】:

    您可以尝试REST Assured,这使得测试 REST 服务和验证 Java 响应(使用 JUnit 或 TestNG)变得非常简单。

    【讨论】:

    • 我对你的帖子投了赞成票,因为图书馆看起来不错,但他们确实使用了很多依赖的 jars...
    【解决方案7】:

    正如詹姆斯所说;泽西岛有内置的test framework。一个简单的 hello world 示例可以是这样的:

    pom.xml 用于 maven 集成。当你运行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

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

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

    【讨论】:

    • 对于 Jersey 2.29.1,我必须添加 jersey-hk2 作为依赖项,因为我收到了一个 java.lang.IllegalStateException: InjectionManagerFactory not found 错误(请参阅 this question)。否则此示例运行良好。
    【解决方案8】:

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

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

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

    【讨论】:

      【解决方案9】:

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

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

      第一个是用 Arquillian 解决的。 第二个完美描述在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. 测试开始时,Arquillian 的跑步者开始工作。 Here 您可以找到如何使用所需的应用程序服务器为 Arquillian 配置测试。
      4. 根据所选的应用程序服务器,在 测试会有所不同。可以使用另一个端口。 8181 是 Glassfish Embedded 在我的示例中使用。

      希望,它会有所帮助。

      【讨论】:

      • 内置的 jersey 测试框架是否支持这种类型的东西?如果我已经有了 Jersey 可用的东西,我只是犹豫要不要将“又一个框架”及其所有 jar 添加到我的项目中。
      【解决方案10】:

      保持简单。看看可以从 Maven Central 导入的 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...
      

      【讨论】:

        猜你喜欢
        • 2018-05-07
        • 1970-01-01
        • 2013-06-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多