[英]Testing a JAX-RS Web Service?
我目前正在尋找為基於JAX-RS (RESTful Web 服務的 Java API)的 Web 服務創建自動化測試的方法。
我基本上需要一種方法來向它發送某些輸入並驗證我是否得到了預期的響應。 我更喜歡通過 JUnit 來做到這一點,但我不確定如何實現。
您使用什么方法來測試您的網絡服務?
更新:正如 entzik 所指出的,將 Web 服務與業務邏輯解耦允許我對業務邏輯進行單元測試。 但是,我還想測試正確的 HTTP 狀態代碼等。
Jersey帶有一個很棒的 RESTful 客戶端 API,可以讓編寫單元測試變得非常容易。 請參閱 Jersey 附帶的示例中的單元測試。 我們使用這種方法來測試Apache Camel 中的 REST 支持,如果您有興趣, 測試用例在這里
您可以試用REST Assured ,這使得測試 REST 服務和驗證 Java 響應變得非常簡單(使用 JUnit 或 TestNG)。
正如詹姆斯所說; 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 {
}
你好世界
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);
}
}
您可以查看此示例應用程序。
您可能編寫了一些實現業務邏輯的 Java 代碼,然后為它生成了 Web 服務端點。
要做的一件重要事情是獨立測試您的業務邏輯。 由於它是純 Java 代碼,因此您可以使用常規的 JUnit 測試來做到這一點。
現在,由於 Web 服務部分只是一個端點,您要確保生成的管道(存根等)與您的 Java 代碼同步。 您可以通過編寫調用生成的 Web 服務 java 客戶端的 JUnit 測試來做到這一點。 這將讓您知道何時更改 Java 簽名而不更新 Web 服務內容。
如果您的 Web 服務管道是由您的構建系統在每次構建時自動生成的,那么可能沒有必要測試端點(假設它全部正確生成)。 取決於你的偏執程度。
盡管從發布問題之日起為時已晚,但認為這可能對有類似問題的其他人有用。 Jersey 附帶了一個名為Jersey 測試框架的測試框架,它允許您測試 RESTful Web 服務,包括響應狀態代碼。 您可以使用它在輕量級容器(如 Grizzly、HTTPServer 和/或 EmbeddedGlassFish)上運行測試。 此外,該框架還可用於在常規 Web 容器(如 GlassFish 或 Tomcat)上運行您的測試。
我使用 Apache 的HTTPClient (http://hc.apache.org/)來調用 Restful 服務。 HTTP 客戶端庫允許您輕松執行獲取、發布或您需要的任何其他操作。 如果您的服務使用 JAXB 進行 xml 綁定,您可以創建一個 JAXBContext 來序列化和反序列化來自 HTTP 請求的輸入和輸出。
看看Alchemy 休息客戶端生成器。 這可以在幕后使用 jersey 客戶端為您的 JAX-RS web 服務類生成代理實現。 實際上,您將從單元測試中將 Web 服務方法稱為簡單的 java 方法。 也處理 http 身份驗證。
如果您需要簡單地運行測試,則不涉及代碼生成,因此很方便。
免責聲明:我是這個庫的作者。
把事情簡單化。 查看可以從 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...
一個重要的事情是獨立測試你的業務邏輯
我當然不會假設編寫 JAX-RS 代碼並希望對接口進行單元測試的人不知何故,出於某種奇怪的、莫名其妙的原因,不知道他或她可以對程序的其他部分進行單元測試,包括業務邏輯類。 陳述顯而易見的事情幾乎沒有幫助,而且人們反復強調響應也需要測試。
Jersey 和 RESTEasy 都有客戶端應用程序,在 RESTEasy 的情況下,您可以使用相同的注解(甚至排除帶注釋的接口並在測試的客戶端和服務器端使用)。
REST 不是這項服務能為您做什么; REST 你可以為這個服務做什么。
據我了解,此問題作者的主要目的是將 JAX RS 層與業務層分離。 並且單元測試只有第一個。 這里我們必須解決兩個基本問題:
第一個是用 Arquillian 解決的。 第二個在arquillican 和 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));
}
}
一些注意事項:
希望,它會有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.