我目前正在寻找为基于JAX-RS(RESTful Web 服务的 Java API)的 Web 服务创建自动化测试的方法。
我基本上需要一种方法来向它发送某些输入并验证我是否得到了预期的响应。我更喜欢通过 JUnit 来实现,但我不确定如何实现。
您使用什么方法来测试您的 Web 服务?
更新:正如 entzik 指出的,将 Web 服务与业务逻辑分离允许我对业务逻辑进行单元测试。但是,我还想测试正确的 HTTP 状态代码等。
我目前正在寻找为基于JAX-RS(RESTful Web 服务的 Java API)的 Web 服务创建自动化测试的方法。
我基本上需要一种方法来向它发送某些输入并验证我是否得到了预期的响应。我更喜欢通过 JUnit 来实现,但我不确定如何实现。
您使用什么方法来测试您的 Web 服务?
更新:正如 entzik 指出的,将 Web 服务与业务逻辑分离允许我对业务逻辑进行单元测试。但是,我还想测试正确的 HTTP 状态代码等。
Jersey带有一个很棒的 RESTful 客户端 API,它使编写单元测试变得非常容易。请参阅 Jersey 附带的示例中的单元测试。我们使用这种方法来测试Apache Camel中的 REST 支持,如果您有兴趣,测试用例在这里
您可以试用REST Assured,这使得在 Java 中测试 REST 服务和验证响应变得非常简单(使用 JUnit 或 TestNG)。
正如詹姆斯所说;Jersey有内置的测试框架。一个简单的 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);
}
}
您可以查看此示例应用程序。
您可能编写了一些实现业务逻辑的 java 代码,然后为它生成了 Web 服务端点。
要做的一件重要事情是独立测试您的业务逻辑。由于它是纯 Java 代码,因此您可以通过常规 JUnit 测试来做到这一点。
现在,由于 Web 服务部分只是一个端点,您要确保生成的管道(存根等)与您的 java 代码同步。您可以通过编写调用生成的 Web 服务 Java 客户端的 JUnit 测试来做到这一点。这将在您更改 Java 签名而不更新 Web 服务内容时通知您。
如果您的 Web 服务管道是由您的构建系统在每次构建时自动生成的,那么可能没有必要测试端点(假设它们都已正确生成)。取决于你的偏执程度。
尽管从发布问题之日起为时已晚,但认为这可能对其他有类似问题的人有用。Jersey 附带一个称为Jersey 测试框架的测试框架,它允许您测试 RESTful Web 服务,包括响应状态代码。您可以使用它在 Grizzly、HTTPServer 和/或 EmbeddedGlassFish 等轻量级容器上运行测试。此外,该框架还可用于在 GlassFish 或 Tomcat 等常规 Web 容器上运行测试。
我使用 Apache 的HTTPClient (http://hc.apache.org/)来调用 Restful Services。HTTP 客户端库允许您轻松执行 get、post 或您需要的任何其他操作。如果您的服务使用 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));
}
}
几点注意事项:
希望,它会有所帮助。