使用Maven的测试 - 组织单元和集成测试
-
Testing with Maven - Organizing unit and integration tests
- Introduction
- How does a Maven project look like?
- What does Maven Surefire plugin do?
- Why isn't Surefire plugin good for running integration tests?
- Maven Failsafe Plugin
- How does Maven Failsafe plugin work?
- How to organize tests properly in your Maven project?
- Summary
- Follow Rodnan Sol for more
- Follow the author too
介绍
您是否正在项目中进行单元和集成测试?但愿如此!
测试是一个良好的工作和高质量软件的基本面,即使他们的客户说ð¥。
也没有人跳过它们。如果您是Java开发人员,您可能知道那里的不同构建工具及其对写作和运行单元和集成测试的支持:
- ant
- Maven
- gradle
- 你命名
在这篇文章中,我们将重点关注由ASF(Apache Software Foundation)构建和维护的 Maven 。
Maven是一个美丽的项目,它帮助数百万个项目进行了生效,这是一个行业标准,但即使数百万个项目都取决于它,也没有什么是完美的。在这篇文章中,我们不会找到Maven构建工具背后的所有负面影响,而只是为了专注于测试主题,并为您提供建议如何设置项目结构以实施更好的测试和生产代码质量。<<<<<<<<<<<<<<<<<<<<< /p>
如果您熟悉Maven项目的设置,并且您知道如何像Surefire和FailSafe这样的不同插件处理编译的测试类,那么您可以跳到以下section,最初的几个只是引入了。 Maven如何处理测试。如果您想刷新您的知识,也可以阅读第一章! :)
Maven项目的样子如何?
默认情况下,如果一个新项目刚刚初始化,则应该像这样:
UserService
类只是为了示例,它们背后没有框架,没有真正的逻辑,仅是为了演示。
我们如何执行项目中的测试?这很容易,只需在您的终端中运行mvn test
命令即可。
$ mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.rodnansol:simple-test-setup >-------------------
[INFO] Building simple-test-setup 1.0-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- resources:3.3.0:resources (default-resources) @ simple-test-setup ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- compiler:3.10.1:compile (default-compile) @ simple-test-setup ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- resources:3.3.0:testResources (default-testResources) @ simple-test-setup ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO]
[INFO] --- compiler:3.10.1:testCompile (default-testCompile) @ simple-test-setup ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- surefire:3.0.0-M8:test (default-test) @ simple-test-setup ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.rodnansol.user.UserServiceTest
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.645 s - in org.rodnansol.user.UserServiceTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.629 s
[INFO] Finished at: 2023-06-04T17:45:29+02:00
[INFO] ------------------------------------------------------------------------
在这种情况下发生了什么?
maven具有多个阶段可以执行的,因为Maven具有pom.xml
文件,它还具有一个所谓的有效POM ,其中包含所有阶段配置,但是它们是没有在pom.xml
文件中呈现,因为那太冗长了,很难阅读。这些配置是从所谓的 super pom 。
有效的POM 包含在执行特定阶段时将运行的所有插件,这些阶段由插件支持,例如 maven-clean-plugin , Maven-Compiler-Plugin 和 maven-surefire-plugin 正在执行任何测试。
不在这里, Maven-Surefire-Plugin 在test
阶段执行其test
目标。即使他们的措辞相同,也不相同。另一件事也至关重要,我们在这里看不到maven-failsafe-plugin
,这将是稍后的关键。
好吧,好吧,我想用眼睛看到它,那么如何创建有效的pom?很容易:mvn help:effective-pom
。
当它超过250行时,让我复制它的一小部分。
我们可以看到 maven-clean-plugin的配置和 maven-surefire-plugin :
Maven SureFire插件可以做什么?
tl; dr:它执行测试。
认真地,它正在执行测试,但是如何识别测试?
插件具有配置值,其中一个首先定义了编译测试类的文件夹,而其他配置选项设置了随附的文件,它主要是通过模式完成的。
- 测试类目录:https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#testclassesdirectory
- 默认情况下:
target/test-classes
-由 maven-compiler-plugin 's testompile 目标 expile 阶段。肯定有很多事情要去那里。 ð¥
- 默认情况下:
- 包括课程:https://maven.apache.org/surefire/maven-surefire-plugin/test-mojo.html#includes
- 默认情况下任何以:test *.java *test.java, *tests.java, *testcase.java。
现在可能是 aha!时刻,“所以这就是为什么我闪亮的Junit测试未执行的原因,因为我将其命名为Userservicet或Userservicete”。
是!但是您可以将其配置为包括新模式或可以将特定文件放入包含部分。
。为什么SureFire插件不适合运行集成测试?
如果我们检查默认生命周期,我们将看到可用阶段的列表:
让我们再次检查test
阶段。在此阶段之前,我们只能看到编译测试类所必需的test-compile
,我们肯定会看到其他一些阶段,但现在对我们来说并不是那么“有趣”。
之后,我们看不到“测试”相关阶段,但是integration-test
阶段,默认情况下与Surefire插件无关。
如果我们要运行集成测试,那将需要不同的环境设置,例如在运行任何集成测试之前运行 docker-compose.yml 文件,并在集成测试时停止启动依赖项完成?
Maven SureFire不是为运行集成测试而创建的,这就是为什么它不拾取具有IT的测试类,其名称中的ITCase Postfix,因为应该使用Maven Failsafe插件! < /p>
ð在这里一个小注:您绝对可以配置Surefire插件来处理以ITCase结尾的测试类,但是它并不是要与它们一起使用。另一方面,集成测试也可以称为简单测试,测试,测试柜,但它们不同,请不要混合您的单元和集成测试。
Maven FailSafe插件
让我引用以下内容中的以下文档:
“在SureFire插件旨在运行单元测试的同时,FailSafe插件旨在运行集成测试。选择了名称(FailSafe),因为它是SureFire的同义词,并且因为它意味着当失败时,它确实可以所以以安全的方式。
Maven生命周期有四个用于运行集成测试的阶段:
- 用于设置集成测试环境的预融合测试
- 集成测试用于运行集成测试。
- 综合测试后用于拆除集成测试环境。
- 验证用于检查集成测试的结果。
如果您使用SureFire插件进行运行测试,则当您患有测试故障时,构建将在集成测试阶段停止,并且您的集成测试环境将无法正确拆除。
在集成测试期间使用了故障保护插件,并验证构建生命周期的阶段以执行应用程序的集成测试。在集成测试阶段,FAILSAFE插件不会使构建失败,从而使后整合测试阶段可以执行。” - 来源:https://maven.apache.org/surefire/maven-failsafe-plugin/
无法更好地描述:
- 用于单位测试:使用 surefire !
- 进行集成测试:使用 FailSafe !
Maven FailSafe插件如何工作?
它与 surefire 插件相同,它从特定目录执行测试,并具有特定的文件名模式。
- 测试类目录:https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#testclassesdirectory
- 与 surefire 相同,因此
target/test-classes
,编译的源文件也由 maven编译器插件编译
- 与 surefire 相同,因此
- 包括课程:https://maven.apache.org/surefire/maven-failsafe-plugin/integration-test-mojo.html#includes
- 这是可以轻松看到差异的地方,而 surefire 插件包含Java类,以测试,测试,testcase后缀结尾,在这种情况下,以下模式为默认模式:IT*.java , *it.java, *itcase.java。 - 因此例如
UserServiceIT.java
或CreateUserITCase.java
。
- 这是可以轻松看到差异的地方,而 surefire 插件包含Java类,以测试,测试,testcase后缀结尾,在这种情况下,以下模式为默认模式:IT*.java , *it.java, *itcase.java。 - 因此例如
这里的要点是,默认情况下,必须将集成测试放入“相同”文件夹结构中,因此必须放入src/tests
文件夹。默认情况下没有src/it
或src/it-test
选项,但是它可以轻松更改,等到结束。
如何在您的Maven项目中正确组织测试?
我们有一些选择,我将向您展示3我知道的3,我将尝试定义不同设置的优点和缺点。
我想在这里做笔记,我们的目标是不要打破包装设置,因此每次我们为班级编写单元或集成测试时,我们都想反映这样的生产和测试代码包:
因此,如果我们有这样的包:org.rodnansol.user
,并且其中有一个名为UserService
的课程,我们的UserServiceTest
或UserServiceIT
也应该在org.rodnansol.user
软件包中。
在某些情况下,我们的集成测试(因为我们为其命名)在级别测试金字塔中涵盖了不同的水平,例如全端2端测试,在这种情况下,这是另一个主题,但是如果类是另一个主题您考虑一下,在这种情况下,您可能不知道要测试的代码,在这种情况下,它可能是一个黑框,这取决于您的设置。
快速扰流板:在第二个选项中,我们将破坏它,但您会看到。
不要忘记设置Maven FailSafe插件来运行测试!
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M8</version>
<executions>
<execution>
<id>default-integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
</plugin>
混合单元和集成测试
顾名思义,将设备和集成测试混合在一起,只是将它们放置在彼此相邻的情况下。
优点
- 可以很容易地彼此发现测试。
- 封装不应被打破,因此,如果仅在
user
软件包中使用UseService
,则可以是 package private 。仅仅因为我们正在为类写一个测试,这并不意味着封装应该被打破,而是类中的方法,字段,常数也是如此。始终为最狭窄的范围工作,不要仅仅因为测试需要一些东西而扩展它。- 是的,如果您不知道,生产和测试代码中的可见性并没有差异,因此测试可以访问生产代码的私人或受保护成员的软件包!明智地使用它!
缺点
- 单元和集成测试混合在一起,它可能会污染代码,如果集成测试具有不同的实用程序类,则可能最终在这些类别旁边,并且随着时间的时间而没有适当的维护,设置可能会造成混乱。<<<<<<<<<<<<<<<< /li>
包装级别的单独单元和集成测试
我已经在许多项目上看到了它,如果我们有这样的包结构:org.rodnansol.user
开发人员引入了一个中间软件包,该软件包旨在通过集成测试使用,例如:org.rodnansol.it.user
或org.rodnansol.integrationtest.user
。
优点
- 单位和集成测试分开。
- 集成测试相关的实用程序可以放在集成测试旁边,并且可以将其标记为私有的包装,以确保其他测试不会使用它。
缺点
- 封装被打破了!不仅有点,而且太多了!
- 从现在开始我们的UserService类都无法包装。
- 都不能在Integration Test中使用的包裹私有化方法。
- 如果我们想与一些依赖关系进行嘲笑,如果它们与软件包私有类别相同的包中,现在必须公开来进行测试。
即使似乎很想使用这种设置,也请考虑生产代码中的后果。可见性修饰符被创建为明智,正确地使用。仅仅因为测试需要测试方法,就不可能改变其可见性修饰符的理由!我将其称为代码气味!
请在使用此选项之前三思而后行。
单独的单元和集成测试或根文件夹级别
从我的角度来看,可能是“最清洁”的解决方案!将单位和集成测试分开,如下图所示。
。此设置的主要问题是,它没有某种配置就无法正常工作。 maven编译器插件在以下目录中编译Java类:
src/main/java
src/test/java
因此,如果我们想引入第三个文件夹,我们应该将更改甚至是 maven编译器插件,,否则我们必须欺骗系统,即使 Maven编译器插件的默认配置未被覆盖我们的Java类和资源文件,可以通过 Maven FailSafe插件
来使用。如何设置它?
首先,为您的项目创建以下文件夹串件:
-
<project-root-level>
- src
- 它
- java
- 资源
- 主要
- java
- 资源
- 测试
- java
- 资源
Maven编译器插件方式
覆盖 maven-compiler-plugin的默认配置以及对于test-compile
阶段覆盖compileSourceRoots
配置值,并添加以下条目:
<compileSourceRoot>src/test/java</compileSourceRoot>
<compileSourceRoot>src/it/java</compileSourceRoot>
要确保将任何相关的资源(不是Java文件)复制到target/test-classes
文件夹中,我们也必须配置<testResources>
标签,以也包括src/it/resources
文件夹中的资源。它由 maven Resources插件。
查看完整的插件配置:
<build>
<testResources>
<testResource>
<directory>src/it/resources</directory>
</testResource>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<executions>
<execution>
<id>default-testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
<configuration>
<compileSourceRoots>
<compileSourceRoot>src/test/java</compileSourceRoot>
<compileSourceRoot>src/it/java</compileSourceRoot>
</compileSourceRoots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M8</version>
<executions>
<execution>
<id>default-integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
构建助焊剂 - 武器的方式
在这种情况下,不应覆盖 maven-compiler-plugin ,但是必须将新插件引入整个项目,称为 build> build> build helper-helper-maven-plugin < /strong>。
文件夹的设置与以前相同,但是在这种情况下,使用插件,我们将两个目标连接到以下阶段:
- 生成测试源 - “生成任何用于包含在汇编中的测试源代码”。 - 随着这个阶段的绑定,我们的类别从
src/it/java
绑定了,将在测试阶段进行编译。 - 生成测试资源 - “创建用于测试的资源”。 - 随着此阶段绑定,所有集成测试相关的资源文件将复制到
target/test-classes
文件夹。- 使用
<filtering>
选项,如果任何资源文件都包含可变替换($ {})Maven将过滤这些值并用pom.xml
中的值替换它们 - 这是可选的。
- 使用
查看完整的插件配置:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>add-integration-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/it/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-integration-test-resources</id>
<phase>generate-test-resources</phase>
<goals>
<goal>add-test-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/it/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.0.0-M8</version>
<executions>
<execution>
<id>default-integration-test</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在任何插件设置后,以下命令应将任何测试源和资源复制到target/test-classes
文件夹:mvn clean verify
。
verify
阶段将触发 maven failsafe插件在integration-test
阶段中的integration-test
目标。
小注:
确保src/test
和src/it
下的文件有不同的名称,因为它们会互相覆盖,并且会引起惊喜。
优点
- 单位和集成测试分开。
- 集成测试相关的实用程序可以放在集成测试旁边,并且可以将其标记为私有的包装,以确保其他测试不会使用它。
- 封装不会被打破,如果包装正确,则设备和集成测试都可以访问允许的类。
缺点
- 必须更改 Maven编译器插件必须引入第三方Maven插件,名为 build-helper-maven-plugin 必须引入以帮助进行必要的设置。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< /li>
惊喜选项:新的Maven模块
我不会详细介绍,但是我已经看到了一些项目,其中集成测试不是实际的Maven模块的一部分,但是它们位于仅包含集成测试的新模块中。
优点
- 单位和集成测试分开。
- 集成测试相关的实用程序可以放在集成测试旁边,并且可以将其标记为私有的包装,以确保其他测试不会使用它。
缺点
- 新的Maven模块必须取决于它正在通过集成测试进行验证的模块,因此可以在不验证其行为的情况下构建正在测试的Maven模块。您决定这是优势还是不利。进行测试的全部目的是确保如果其行为存在问题,则不会包装工件。
概括
哇!那是一段漫长的旅程,希望您发现它有用,并且将在您的下一个项目设置ð
中使用此设置- 我们了解了Maven的工作原理以及它如何生成和使用其有效的POM 来建立其生命周期。
- 我们了解了 surefire 和 failsafe 插件工作以及它们如何不同。
- 我们学习了三种详细设置单元和集成测试的方法。
- 我们简要学习了第四种方法。
我很好奇您对此的看法,如果您已经做到了!
您如何设置项目?
- 您看到相似之处吗?
- 您目前的设置面临哪些问题?
- 您会考虑将单位和集成测试分开吗?