unit testing一个JAX-RS Web服务?
我目前正在寻找方法来为JAX-RS (基于REST风格的Web服务的Java API)的Web服务创build自动化testing。
我基本上需要一种方式来发送一定的input,并validation我得到了预期的回应。 我宁愿通过JUnit来做到这一点,但我不知道如何实现。
你用什么方法来testing你的networking服务?
更新:正如entzik所指出的那样,将Web服务从业务逻辑中分离出来让我能够对业务逻辑进行unit testing。 不过,我也想testing正确的HTTP状态码等。
Jersey带有一个很好的RESTful客户端API,使得编写unit testing非常简单。 请参阅泽西岛附带的示例中的unit testing。 我们使用这种方法来testingApache Camel中的REST支持,如果你对这个testing用例感兴趣的话
您可以尝试REST Assured ,这使得testingREST服务和validationJava中的响应(使用JUnit或TestNG)变得非常简单。
正如詹姆斯所说; 泽西岛有内置的testing框架 。 一个简单的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> ...
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); } }
你可以检查这个示例应用程序。
虽然从发布这个问题的date已经太晚了,但是对于其他有类似问题的人来说这可能是有用的。 Jersey带有一个名为Jersey Test Framework的testing框架 ,它允许您testing您的REST风格的Web服务,包括响应状态代码。 您可以使用它来在像Grizzly,HTTPServer和/或EmbeddedGlassFish这样的轻量级容器上运行testing。 此外,该框架可以用来在GlassFish或Tomcat等常规Web容器上运行testing。
你可能写了一些实现你的业务逻辑的Java代码,然后你已经为它生成了Web服务端点。
一个重要的事情是独立testing你的业务逻辑。 由于它是纯Java代码,所以您可以使用常规的JUnittesting来完成此操作。
现在,由于Web服务部分只是一个终点,所以您要确保生成的pipe道(存根等)与您的java代码同步。 您可以通过编写调用生成的Web服务Java客户端的JUnittesting来实现这一点。 这将让你知道,当你改变你的Java签名而不更新Web服务的东西。
如果您的Web服务pipe道是由您的构build系统在每个构build中自动生成的,那么可能不需要testing端点(假设它们都已正确生成)。 取决于你的偏执水平。
我使用Apache的HTTPClient(http://hc.apache.org/)来调用Restful Services。 HTTP客户端库允许您轻松执行获取,发布或其他任何您需要的操作。 如果您的服务使用JAXB进行xml绑定,则可以创build一个JAXBContext来对来自HTTP请求的input和输出进行序列化和反序列化。
看看Alchemyrest客户端生成器 。 这可以在场景后面使用泽西客户端为您的JAX-RS webservice类生成代理实现。 实际上,您将从unit testing中将您的webservice方法作为简单的java方法调用。 处理httpauthentication。
如果你需要简单地运行testing,所以不需要代码生成,所以很方便。
免责声明:我是这个图书馆的作者。
一个重要的事情是独立testing你的业务逻辑
我当然不会认为编写JAX-RS代码的人想要对接口进行unit testing是出于某种奇怪的,不可理解的原因,忘记了他或她可以unit testing程序的其他部分,包括业务逻辑类。 说明这个问题是毫无帮助的,反复提出的观点也需要进行检验。
Jersey和RESTEasy都有客户端应用程序,在RESTEasy的情况下,您可以使用相同的注释(甚至将注释的接口分解并在testing的客户端和服务器端使用)。
REST不是这个服务可以为你做什么; REST你可以为这项服务做些什么。
把事情简单化。 看看可以从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. Eg: 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...
据我所知,这个问题的作者的主要目的是分离JAX RS层的业务之一。 而unit testing只有第一个。 我们必须解决两个基本问题:
- 运行testing一些networking/应用程序服务器,将JAX RS组件放入其中。 只有他们。
- 在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 ie 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)); } }
几个注意事项:
- 这里使用了没有web.xml的JAX RSconfiguration。
- 这里使用的是JAX RS Client(没有RESTEasy / Jersey,它们暴露更方便的API)
- 当testing开始时,Arquillian的跑步者开始工作。 在这里你可以find如何configurationArquilliantesting所需的应用程序服务器。
- 根据所选的应用程序服务器,testing中的url会有所不同。 另一个端口可能被使用。 在我的例子中Glassfish Embedded使用8181。
希望,这将有所帮助。