我正在编写一个JAX-RS(Jersey + Maven)应用程序,它可以执行一些棘手的操作(例如调用WAR中嵌入的本机可执行文件)。我需要在服务器上运行[部分]单元测试(JUnit4)(运行Tomcat 7.0.22的Amazon Elastic Beanstalk)来检查一切是否正常。
除了RYO(滚动你自己的)之外,还有一种标准的,灵活的方式吗?我发现的东西似乎更多地与开发人员机器上的集成测试(即Jersey测试框架)有关。甚至RYO让我感到困惑......我怎样才能从源包中调用测试包中的代码?
基本上,我想创建一个我可以调用的/ test资源,它将以漂亮的格式从服务器返回我的单元测试结果。如果我可以做/ test / {category}
,那就更好了答案 0 :(得分:27)
我想分享我在发布此问题后所学到的内容,并在StackExchange上发布我的第一个答案(我通过谷歌无数次到达的网站寻找无穷无尽问题的解决方案)
在这个问题上有很多纠正,争论和拖钓,所以我想清理它。这一切都非常简单。说你有一些服务。当你打电话给它时,我会简单地说明一系列事件:
(收到请求) - (调用函数1) - (调用函数2) - (调用函数3) - (发送响应)
单元测试单独测试每个功能(或类或单元),输入输入并检查输出。集成测试需要几个单元(例如功能2功能3链),并且也可以使用ol'进进出出。功能测试贯穿整个链,从请求到响应。我将把它留给读者来猜测在每个规模水平上测试的一些优点和缺点。无论如何,所有这些测试都可以在服务器上运行,并且有很好的理由来运行它们。
还有一点要做。 Netbeans将Maven测试的大部分好处提供给WAR测试。它包括一个嵌入式服务器,并在构建后自动启动和部署。它甚至可以打开Firefox ......只需将其设置为指向您的/测试资源即可。它就像用Maven那样做,但更好。
无论如何,我将向您展示如何在同一个Maven项目中一起进行Maven测试和内部测试。
Spring是一个庞大的容器框架。它的依赖注入机制与Jax-RS交织在一起,以显着的学习曲线为代价,产生了辉煌的效果。我不解释Spring或Jax-RS是如何工作的。我会直接跳到指令中,希望读者可以将这些想法调整到其他场景中。
在JUnit 4测试中获取容器的方法是使用Spring测试运行器,声明要在容器中注册的类,注册一些特定于Jax-RS的帮助程序类,注册您的模拟,最后使用你的Jax-RS资源,好像它是一个普通的类:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes={
MyClass1.class,
Myclass2.class,
MyJaxRsResource.class,
MockServletContextAwareProcessor.class,
MyCTest.Config.class
})
public class MyCTest
{
@Configuration
static class Config
{
// Set up and register mocks here, and watch them be autowired!
@Bean public DBService dbJobService() throws DBException
{
return mock(DBService.class);
}
}
@Autowired MyJaxRsResource myResource;
@Test public void test() {
String response = myResource.get("hello");
}
}
@WebAppConfiguration
注入自己的ServletContextAwareProcessor。但是,当必须动态设置解压缩WAR文件的路径时,MockServletContextAwareProcessor
是必需的,因为WebAppConfiguration只允许您在编译时静态设置路径。在运行服务器中的测试时使用这个类(见下文),我注入了真正的ServletContext。我使用Spring的配置文件功能通过环境变量来抑制它(这不是很优雅)。 setServletContext只由服务器测试运行器调用。
@Configuration
public class MockServletContextAwareProcessor {
public static void setServletContext(ServletContext sc) {
servletContext = sc;
}
private static ServletContext getServletContext() {
return servletContext;
}
private static ServletContext servletContext;
@Configuration
@Profile("server-test")
static class ServerTestContext {
static public @Bean
ServletContextAwareProcessor
scap() {
ServletContext sc = getServletContext();
return new ServletContextAwareProcessor(sc);
}
}
}
步骤1)在/ src / test文件夹中创建常规JUnit测试,但将它们命名为IT * .java或* IT.java或* ITCase.java(例如,MyClassIT.java)您可以使用不同的名称命名它们,但是这样是Failsafe默认的预期。 IT代表集成测试,但测试代码可以位于测试连续体的任何位置。例如,您可以实例化一个类并对其进行单元测试,或者您可以启动HttpClient(或Jersey客户端),将其指向您自己(请注意下面的端口),并在功能上测试您的入口点。
public class CrossdomainPolicyResourceSTest extends BaseTestClass {
static com.sun.jersey.api.client.Client client;
@BeforeClass public static void
startClient() {
client = Client.create();
}
@Test public void
getPolicy() {
String response =
client
.resource("http://localhost/crossdomain.xml")
.get(String.class);
assertTrue(response.startsWith("<?xml version=\"1.0\"?>"));
}
}
BaseTestClass
只是一个小帮助类,它打印测试类的名称并在执行时进行测试(对于在服务器中测试很有用,见下文):
public abstract class BaseTestClass {
@ClassRule public static TestClassName className = new TestClassName();
@Rule public TestName testName = new TestName();
@BeforeClass public static void
printClassName() {
System.out.println("--" + className.getClassName() + "--");
}
@Before public void
printMethodName() {
System.out.print(" " + testName.getMethodName());
}
@After public void
printNewLine() {
System.out.println();
}
}
步骤2)将maven-failsafe-plugin和maven-jetty-plugin添加到你的pom.xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.11</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.26</version>
<configuration>
<!-- By default the artifactId is taken, override it with something simple -->
<contextPath>/</contextPath>
<scanIntervalSeconds>2</scanIntervalSeconds>
<stopKey>foo</stopKey>
<stopPort>9999</stopPort>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>9095</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<scanIntervalSeconds>0</scanIntervalSeconds>
<daemon>true</daemon>
</configuration>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
步骤3)利润。真的,那就是它!只需运行&#39; mvn install&#39;或者在IDE中点击构建,并且代码将构建,您的常规* Test.java测试将运行,jetty服务器将启动,* IT.java测试将运行,并且您将获得一个很好的报告
(与上述说明一起使用或分开使用)
步骤1)通过指示maven-war-plugin包含它们来获取WAR中嵌入的测试类(src / test /目录):(改编自here)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<webResources>
<resource>
<directory>${project.build.directory}/test-classes</directory>
<targetPath>WEB-INF/classes</targetPath>
</resource>
<resource>
<directory>${project.build.directory}/test-libs</directory>
<targetPath>WEB-INF/lib</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
注意:您可以通过创建额外的执行并在其配置集中创建一个单独的WAR以及(我留给读者的详细信息)
注意:理想情况下,上面会排除所有常规测试(并且只复制* IT.java)但是,我无法获得包含/排除工作。
您还必须通过为maven-dependency-plugin提供额外的执行来包含测试库,其目标是包含测试范围的复制依赖
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<excludeScope>compile</excludeScope>
<outputDirectory>${project.build.directory}/test-libs</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
</configuration>
</execution>
</executions>
</plugin>
如果maven-dependency-plugin已经有其他执行(例如,Netbeans为javaee-endorsed-api插入一个),请不要删除它们。
步骤2)使用JUnitCore(JUnit4)以编程方式运行测试。
String runTests() {
PrintStream sysOut = System.out;
PrintStream sysErr = System.err;
ByteArrayOutputStream stream = new ByteArrayOutputStream();
PrintStream out = new PrintStream(stream);
try {
System.setOut(out);
System.setErr(out);
TextListener listener = new TextListener(out);
JUnitCore junit = new JUnitCore();
junit.addListener(listener);
junit.run(MyClassIT.class,
AnotherClassIT.class,
...etc...);
} finally {
System.setOut(sysOut);
System.setErr(sysErr);
out.close();
}
return stream.toString();
}
步骤3)通过JAX-RS公开测试
@Path("/test")
public class TestResource {
@GET
@Produces("text/plain")
public String getTestResults() {
return runTests();
}
private String runTests() {
...
}
}
将此类与其他测试类(在src / test中)一起放置,以便它可以引用它们。
但是,如果您在javax.ws.rs.core.Application类中继承了您注册所有资源的地方,那么您在引用TestResource时会遇到问题(因为源代码可以&#39; t参考测试代码)。要解决这个问题,请在src / main / ... [相同包]下创建一个完全空的虚拟TestResource类...这个技巧有效,因为虚拟TestResource在打包过程中会被真实的TestResource覆盖。
public class ShoppingApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>() {{
add(TestResource.class);
}};
}
@Override
public Set<Object> getSingletons() {
return new HashSet<Object>();
}
}
package ...same package as the real TestResource...
public class TestResource {
}
步骤4)设置IDE以启动/部署您的应用并打开浏览器指向&#34; / test&#34;构建后自动生成。
答案 1 :(得分:3)
获胜关键字原来是“容器内测试”。全新且卓越的框架是Arquillian。
奇怪的是,似乎没有别的东西。 StackOverflow asked上的其他人“我没有看到任何这些项目被广泛使用,所以容器内测试有什么不好吗?”但没有收到明确的答复。
我想这只是单元测试和完整集成测试这两个大范围之间的一个小区域,需要通过容器内测试来解决。对我来说,我只需要一些测试来检查服务器资源是否可访问和正常运行。可能应该手工编写,而不是花时间研究(然后学习)容器内测试。
答案 2 :(得分:1)
使用Maven,Surefire可以为您提供测试结果的格式化报告。
http://maven.apache.org/plugins/maven-surefire-report-plugin/report-mojo.html
可以通过多种方式使这些报告的内容可用,无论这些报告是发送给您还是发布到网页。你有很多选择。
答案 3 :(得分:1)
Jakarta Cactus似乎做了我正在寻找的事情。它的主页说:“Cactus是一个简单的测试框架,用于单元测试服务器端的Java代码...它使用JUnit ... Cactus实现了一个容器内策略......”http://localhost:8080/test/ServletTestRunner?suite=TestSampleServlet之类的URL会提供一个漂亮的HTML输出。
然而,由于缺乏积极的开发,Apache基金会将其置于阁楼中。这是否意味着我不应该考虑使用它? Attic页面说“鼓励Cactus用户切换到其他测试技术”而不解释它们是什么!
答案 4 :(得分:0)
我认为没有标准的方法,但您可以使用Spring Remoting从您的开发人员计算机调查您感兴趣的服务器上的方法。如果您使用接口并注入您正在测试的服务,您应该能够在本地运行两次相同的单元测试,一次在服务器上运行,只需更改Spring配置即可。