1. 概述

Maven 是 Java 生态中最主流的构建工具,而集成测试(Integration Testing)则是保障系统稳定性的关键环节。因此,使用 Maven 来配置和执行集成测试,是一种自然且高效的选择

本文将深入探讨多种在 Maven 项目中实施集成测试的方案,并重点讲解如何将集成测试与单元测试有效分离,避免互相干扰。

你可能已经踩过这样的坑:每次 mvn test 都要启动整个 Web 容器,结果跑个 POJO 测试都要十几秒——这显然不合理。本文的目标就是帮你把这件事做“对”。


2. 环境准备

为了让示例贴近真实项目,我们将构建一个基于 JAX-RS 的 REST 服务,集成测试前自动启动 Jetty 容器,测试完成后自动关闭。

2.1. Maven 配置

我们选用 Jersey 作为 JAX-RS 的实现框架,需引入以下核心依赖:

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet-core</artifactId>
    <version>2.27</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.inject</groupId>
    <artifactId>jersey-hk2</artifactId>
    <version>2.27</version>
</dependency>

✅ 提示:建议前往 MVNRepository 获取最新版本。

为了在集成测试阶段自动启停 Web 服务器,我们使用 Jetty Maven Plugin。它的核心机制是:

  • pre-integration-test 阶段启动 Jetty
  • post-integration-test 阶段停止 Jetty

插件配置如下:

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.4.11.v20180605</version>
    <configuration>
        <httpConnector>
            <port>8999</port>
        </httpConnector>
        <stopKey>quit</stopKey>
        <stopPort>9000</stopPort>
    </configuration>
    <executions>
        <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>start</goal>
            </goals>
        </execution>
        <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>stop</goal>
            </goals>
        </execution>
    </executions>
</plugin>

⚠️ 注意:stopKeystopPort 是 Jetty 插件内部用于优雅关闭的机制,值不重要,但必须存在。

此外,**必须将打包方式设为 war**,否则 Jetty 插件无法加载 Web 应用:

<packaging>war</packaging>

2.2. 创建 REST 接口

我们实现一个极简的 REST 接口,用于返回欢迎信息:

@Path("/")
public class RestEndpoint {
    @GET
    public String hello() {
        return "Welcome to Baeldung!";
    }
}

通过 ResourceConfig 注册该接口类:

package com.baeldung.maven.it;

import org.glassfish.jersey.server.ResourceConfig;

public class EndpointConfig extends ResourceConfig {
    public EndpointConfig() {
        register(RestEndpoint.class);
    }
}

最后,使用标准的 web.xml 将 Jersey 容器部署到 Servlet 容器中:

<web-app 
  xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1">
    <servlet>
        <servlet-name>rest-servlet</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>com.baeldung.maven.it.EndpointConfig</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>rest-servlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

✅ 文件路径必须为 src/main/webapp/WEB-INF/web.xml,这是 Java Web 项目的规范。

2.3. 测试客户端代码

后续所有测试类都包含相同的测试方法,用于验证接口可用性:

@Test
public void whenSendingGet_thenMessageIsReturned() throws IOException {
    String url = "http://localhost:8999";
    URLConnection connection = new URL(url).openConnection();
    try (InputStream response = connection.getInputStream();
      Scanner scanner = new Scanner(response)) {
        String responseBody = scanner.nextLine();
        assertEquals("Welcome to Baeldung!", responseBody);
    }
}

逻辑非常简单:发送 GET 请求并校验响应内容。


3. 集成测试的设计原则

集成测试通常耗时较长(比如启动容器、连接数据库等),因此:

必须将其排除在默认构建流程之外,否则每次 mvn compile 都会拖慢开发反馈速度。

❌ 不要让集成测试成为日常开发的负担。

解决方案是:使用 Maven Profile 来按需触发集成测试。这是业界最常见、最灵活的做法。

下文所有方案均基于 Profile 实现,确保集成测试只在明确指定时才执行。


4. 使用 Failsafe 插件(推荐)

Maven 官方提供了 **maven-failsafe-plugin**,专为集成测试设计,是目前最标准的方案。

maven-surefire-plugin(负责单元测试)不同,failsafe 插件绑定在以下阶段:

  • integration-test:运行集成测试
  • verify:验证测试结果

两者默认识别的测试类命名规则也不同:

插件 匹配模式
Surefire(单元测试) Test*, *Test, *Tests, *TestCase
Failsafe(集成测试) IT*, *IT, *ITCase

只需按命名规范写测试类,Maven 自动分离。

配置方式

<profile>
    <id>failsafe</id>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.1.2</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

✅ 插件最新版见:MVNRepository

示例测试类

public class RestIT {
    // 包含 2.3 节中的测试方法
}

执行命令

mvn verify -Pfailsafe

Maven 执行流程如下:

  1. pre-integration-test → 启动 Jetty
  2. integration-test → 运行 RestIT
  3. post-integration-test → 停止 Jetty

✅ 测试通过。

自定义匹配规则

如果不想用默认命名,可手动指定:

<plugin>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>3.1.2</version>
    <configuration>
        <includes>
            <include>**/*RestIT</include>
            <include>**/RestITCase</include>
        </includes>
    </configuration>
    ...
</plugin>

5. 使用 Surefire 插件(替代方案)

你也可以让 maven-surefire-plugin 承担集成测试任务,通过 Profile 控制执行时机。

场景:使用 *IntegrationTest 后缀

  1. 默认排除 这类测试:
<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <excludes>
            <exclude>**/*IntegrationTest</exclude>
        </excludes>
    </configuration>
</plugin>
  1. 在 Profile 中重新包含,并绑定到 integration-test 阶段:
<profile>
    <id>surefire</id>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <executions>
                    <execution>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <configuration>
                            <excludes>
                                <exclude>none</exclude>
                            </excludes>
                            <includes>
                                <include>**/*IntegrationTest</include>
                            </includes>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

⚠️ 关键点:<exclude>none</exclude> 用于覆盖父配置中的排除规则。

示例测试类

public class RestIntegrationTest {
    // 包含 2.3 节中的测试方法
}

执行命令

mvn verify -Psurefire

6. 使用 Cargo 插件(高级选项)

cargo-maven2-plugin 支持多种嵌入式容器(Tomcat、Jetty、WildFly 等),适合需要模拟生产环境的复杂集成测试。

它能自动部署 WAR 包到目标容器,并支持远程部署。

🔗 详细用法可参考:Integration Testing with the Maven Cargo Plugin

虽然功能强大,但配置较复杂,建议在有明确容器适配需求时再引入。


7. 使用 JUnit @Category(灵活分类)

JUnit 4 提供了 @Category 注解,可用于逻辑上标记测试类型。

步骤

  1. 定义分类接口:
package com.baeldung.maven.it;

public interface Integration { }
  1. 标记测试类:
@Category(Integration.class)
public class RestJUnitTest {
    // 测试方法
}
  1. 在主构建中排除该分类:
<plugin>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <excludedGroups>com.baeldung.maven.it.Integration</excludedGroups>
    </configuration>
</plugin>
  1. 在 Profile 中使用 Failsafe 包含该分类:
<profile>
    <id>category</id>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.1.2</version>
                <configuration>
                    <includes>
                        <include>**/*</include>
                    </includes>
                    <groups>com.baeldung.maven.it.Integration</groups>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</profile>

执行命令

mvn verify -Pcategory

✅ 优势:测试类命名自由,分类更清晰。


8. 独立的集成测试目录(推荐结构)

更彻底的隔离方式是:为集成测试创建独立的源码目录

使用 build-helper-maven-plugin 添加额外测试源:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <version>3.0.0</version>
    <executions>
        <execution>
            <id>add-integration-test-source</id>
            <phase>generate-test-sources</phase>
            <goals>
                <goal>add-test-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/integration-test/java</source>
                </sources>
            </configuration>
        </execution>
        <execution>
            <id>add-integration-test-resource</id>
            <phase>generate-test-resources</phase>
            <goals>
                <goal>add-test-resource</goal>
            </goals>
            <configuration>
                <resources>
                    <resource>
                        <directory>src/integration-test/resources</directory>
                    </resource>
                </resources>
            </configuration>
        </execution>
    </executions>
</plugin>

✅ 目录结构建议:

src/
├── main/
├── test/                 → 单元测试
└── integration-test/     → 集成测试(Java + resources)

示例测试

public class RestITCase {
    // 测试方法
}

执行

mvn verify -Pfailsafe

只要类名符合 Failsafe 规则,测试即可自动执行。


9. 总结

本文系统介绍了在 Maven 项目中实施集成测试的多种方案,核心要点如下:

推荐组合maven-failsafe-plugin + 独立测试目录(src/integration-test
关键原则:集成测试必须通过 Profile 按需执行,避免污染主构建流程
命名规范:使用 *IT*ITCase 是最简单粗暴且被广泛接受的做法
容器管理:Jetty Maven Plugin 适合轻量级测试,Cargo 适合复杂场景

所有示例代码已托管至 GitHub:

🔗 https://github.com/eugenp/tutorials/tree/master/maven-modules/maven-integration-test


原始标题:Integration Testing with Maven | Baeldung