
junit testng
如果您是经验丰富的Java开发人员,他从事测试驱动的开发(希望每个人都这样做),那么很有可能JUnit 4是您的一站式测试工具箱。 就我个人而言,我真的很喜欢它,但仍然喜欢它:简单,最少,非侵入性和直观。 连同出色的库(如Assertj和Hamcrest)一起,它使编写测试用例成为一种乐趣。
但是随着时间的流逝,Java作为一种语言已经发展了很多,但是JUnit 4并没有真正发展起来。 大约在2015年左右, JUnit 5的开发就开始了一个雄心勃勃的目标,即成为下一代针对Java和JVM的程序员友好测试框架。 而且,公平地说,我认为已经实现了这一目标:许多新项目从一开始就采用JUnit 5 ,而旧项目已经在迁移过程中(或至少正在考虑中)。
对于现有项目,向JUnit 5的迁移不会在一夜之间发生,可能会花费一些时间。 在今天的帖子中,我们将讨论借助Apache Maven和Apache Maven Surefire插件维护混合的JUnit 4 / JUnit 5和TestNG / JUnit 5测试套件的方法。
为了使示例更真实一些,我们将测试一个UploadDestination类,该类基本上仅提供一个方法,该方法说明是否支持特定的目标方案:
import java.net.URI;
public class UploadDestination {
public boolean supports(String location) {
final String scheme = URI.create(location).getScheme();
return scheme.equals( "http" ) || scheme.equals( ) || scheme.equals( "s3" ) || scheme.equals( ) || scheme.equals( "sftp" );
}
}
实现者非常友好,可以创建一套JUnit 4单元测试,以验证确实支持所有预期的目标方案。
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith (Parameterized. class )
public class JUnit4TestCase {
private UploadDestination destination;
private final String location;
public JUnit4TestCase(String location) {
this .location = location;
}
@Before
public void setUp() {
destination = new UploadDestination();
}
@Parameters (name= "{index}: location {0} is supported" )
public static Object[] locations() {
return new Object[] { "s3://test" , " http://host:9000 " , "sftp://host/tmp" };
}
@Test
public void testLocationIsSupported() {
assertTrue(destination.supports(location));
}
}
至少在项目构建中,至少需要将JUnit 4依赖项连同Apache Maven Surefire插件以及(可选) Apache Maven Surefire Reporter插件一起添加到pom.xml ,以下代码片段说明了这一点。
< dependencies >
< dependency >
< groupId >junit</ groupId >
< artifactId >junit</ artifactId >
< version >4.13.1</ version >
< scope >test</ scope >
</ dependency >
</ dependencies >
< build >
< plugins >
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-surefire-plugin</ artifactId >
< version >3.0.0-M5</ version >
</ plugin >
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-surefire-report-plugin</ artifactId >
< version >3.0.0-M5</ version >
</ plugin >
</ plugins >
</ build >
毫无疑问,触发Apache Maven构建通常每次都会运行所有单元测试套件。
...
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit4TestCase
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.011 s - in com.example.JUnit4TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
...
太棒了,让我们想象一下,另一个队友正巧在该项目上工作,并注意到没有任何单元测试可以验证不支持的目标方案,因此她使用JUnit 5进行了添加。
package com.example;
import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class JUnit5TestCase {
private UploadDestination destination;
@BeforeEach
void setUp() {
destination = new UploadDestination();
}
@ParameterizedTest (name = "{index}: location {0} is supported" )
@ValueSource (strings = { "s3a://test" , " https://host:9000 " , "ftp://host/tmp" } )
public void testLocationIsNotSupported(String location) {
assertFalse(destination.supports(location));
}
}
因此,另一个依赖项出现在项目的pom.xml中,以引入JUnit 5 (因为其API与JUnit 4不兼容)。
< dependencies >
< dependency >
< groupId >org.junit.jupiter</ groupId >
< artifactId >junit-jupiter</ artifactId >
< version >5.7.0</ version >
< scope >test</ scope >
</ dependency >
</ dependencies >
看起来很合法,不是吗? 但是有一个陷阱……这次的测试结果令人惊讶。
...
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.076 s - in com.example.JUnit5TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
...
JUnit 4测试套件不见了, Apache Maven Surefire团队实际上在官方文档的“提供者选择”部分中很好地证明了这种行为。 那么我们如何才能让他们回来呢? 有几种可能的选择,但是到目前为止,最简单的选择是使用JUnit Vintage引擎,以便使用JUnit 5平台运行JUnit 4测试套件。
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-surefire-plugin</ artifactId >
< version >3.0.0-M5</ version >
< dependencies >
< dependency >
< groupId >org.junit.vintage</ groupId >
< artifactId >junit-vintage-engine</ artifactId >
< version >5.7.0</ version >
</ dependency >
</ dependencies >
</ plugin >
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.079 s - in com.example.JUnit5TestCase
[INFO] Running com.example.JUnit4TestCase
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.009 s - in com.example.JUnit4TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 6 , Failures: 0 , Errors: 0 , Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
从这里可以学到的教训:请仔细观察所有测试套件是否正在执行(CI / CD通常会跟踪此类趋势并立即警告您)。 特别是,在迁移到最新的Spring Boot或Apache Maven Surefire插件版本时,要格外小心。
您可能会遇到的另一个非常常见的用例是在一个项目范围内混合使用TestNG和JUnit 5测试套件。 症状几乎相同,您会想知道为什么只运行JUnit 5测试套件。 这种情况下的处理方式略有不同,并且似乎效果很好的一种选择是显式枚举测试引擎提供者。
< plugin >
< groupId >org.apache.maven.plugins</ groupId >
< artifactId >maven-surefire-plugin</ artifactId >
< version >3.0.0-M5</ version >
< dependencies >
< dependency >
< groupId >org.apache.maven.surefire</ groupId >
< artifactId >surefire-junit-platform</ artifactId >
< version >3.0.0-M5</ version >
</ dependency >
< dependency >
< groupId >org.apache.maven.surefire</ groupId >
< artifactId >surefire-testng</ artifactId >
< version >3.0.0-M5</ version >
</ dependency >
</ dependencies >
</ plugin >
在这种情况下,有些不希望的结果是测试套件是单独运行的(尽管还有其他方法可以尝试),例如:
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
[INFO] Running com.example.JUnit5TestCase
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.074 s - in com.example.JUnit5TestCase
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0
[INFO]
[INFO]
[INFO] -------------------------------------------------------
[INFO] TESTS
[INFO] -------------------------------------------------------
[INFO] Running TestSuite
[INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0 , Time elapsed: 0.315 s - in TestSuite
[INFO]
[INFO] Results:
[INFO]
INFO] Tests run: 3 , Failures: 0 , Errors: 0 , Skipped: 0
[INFO]
[INFO]
[INFO] ------------------------------------------------------------------------
公平地说,我认为JUnit 5是朝着为Java(通常是JVM)项目提供现代且简洁的测试套件迈出的一大步。 如今,几乎可以与任何其他测试框架或库( Mockito , TestContainers …)进行无缝集成,并且在大多数情况下迁移路径并不那么困难。 另外,如您所见, JUnit 5与较早的测试引擎共存是完全可行的。
与往常一样,可以在Github上获得完整的项目示例: JUnit 4 / JUnit 5 , TestNG / JUnit 5 。
junit testng