测试JAX-RS Web服务?


84

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

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

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

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


6
很好的问题-但是我要说的是,如果您正在通过HTTP进行测试,那么令我惊讶的是这是集成测试。
汤姆·达克林

汤姆 你是绝对正确的。为此,我们应该注入一个虚拟的HTTP仿真器/轻量级容器。在node.js中,世界超级测试做到了这一点。您可以模拟express.js。
的Firat KUCUK

Answers:


34

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


6
re:现在是错误的链接您可以在jersey / samples中找到显示单元测试的示例,基本上是通过使用jersey的使用者来消耗Web资源。 download.java.net/maven/2/com/sun/jersey/samples/bookstore/...
rogerdpack

2
该项目位于GitHub上,在src / test文件夹中找到测试:github.com/jersey/jersey/tree/master/examples/bookstore-webapp
Venkat

2
我对此表示怀疑,但是我感到非常有趣的是,Jersey总是进入JAX-RS对话,在某些情况下(不幸的是,准确地说,WebSphere)它不可用,并且提供了所有可接受答案的99%在Stack Overflow上无效。

26

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


1
我对您的帖子投了赞成票,因为图书馆看上去不错,但他们肯定使用了很多依赖的罐子……
Perry Tew 2012年

17

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

pom.xml用于maven集成。当你跑步mvn test。框架开始了一个灰熊的容器。您可以通过更改依赖项来使用码头或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);
    }
}

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


使用Jersey 2.29.1,我必须添加jersey-hk2为依赖项,因为遇到了java.lang.IllegalStateException: InjectionManagerFactory未发现错误(请参阅此问题)。否则,此示例将运行良好。
莎拉N

7

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

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

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

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


2
您说得很对,尽管我还需要测试返回的实际HTTP响应,尤其是HTTP状态代码。
Einar's

6

尽管从发布问题的日期算起为时已晚,但认为这可能对有类似问题的其他人有用。Jersey带有一个称为Jersey测试框架测试框架,该框架允许您测试RESTful Web服务,包括响应状态代码。您可以使用它在轻量级容器(例如Grizzly,HTTPServer和/或EmbeddedGlassFish)上运行测试。同样,该框架可用于在常规Web容器(例如GlassFish或Tomcat)上运行测试。


您是否有一个关于如何模拟调用处理程序的好例子?JerseyHttpCall-> MyResource-> CallHandler.getSomething()我们如何在此处模拟CallHandler?
Balaji Boggaram Ramanarayan 2014年

3

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


3

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

如果您只需要运行测试,则无需生成代码,因此非常方便。

免责声明:我是这个图书馆的作者。


2

把事情简单化。看看可以从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...

1

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

我当然不会认为,出于某种奇怪的,莫名其妙的原因,编写JAX-RS代码并希望对接口进行单元测试的人没有想到他或她可以对程序的其他部分进行单元测试,包括业务逻辑类。陈述明显的观点几乎没有帮助,而且反复指出要对响应进行测试也很重要。

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

REST并非此服务可以为您做的;REST您可以为此服务执行的操作。


人们可能想测试一些横切关注点。例如,验证,身份验证,所需的HTTP标头等。因此,人们可以选择测试其JAX-RS代码。
的Firat KUCUK

在我的应用程序中,我使用ModelMapper将“ DTO”类“映射”到“业务对象”类,基础“服务”类可以理解。这是一个可以独立测试的示例。
jkerak

有时,像我目前的情况一样,REST applet的复杂性很小,以至于模拟比应用程序层还要大。:)
tekHedd

1

据我了解,此问题的作者的主要目的是使JAX RS层与业务层脱钩。而且单元测试只有第一个。这里我们要解决两个基本问题:

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

第一个是与Arquillian解决的。第二个在arquillican和模拟中得到了完美的描述

这是一个代码示例,如果您使用其他应用程序服务器,它可能会有所不同,但是我希望您能获得基本的思想和优点。

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 Client(没有RESTEasy / Jersey,它们公开了更方便的API)
  3. 测试开始时,Arquillian的跑步者开始工作。在这里,您可以找到如何使用所需的应用程序服务器配置Arquillian的测试。
  4. 根据所选的应用程序服务器,测试中的URL会有所不同。可以使用另一个端口。在我的示例中,Glassfish Embedded使用了8181。

希望会有所帮助。


内置的jersey测试框架支持这种类型的东西吗?如果我已经有了Jersey可用的东西,我只是在犹豫是否将“另一个框架”及其所有jar添加到我的项目中。
jkerak
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.