junit数据驱动测试_JUnit 5-早期测试驱动器-第1部分

junit数据驱动测试

重要要点

  • JUnit 5即将发布!
  • 它经过改进的API和扩展模型大大改善了“ JUnit工具”。
  • 模块化体系结构使“ JUnit平台”可用于其他测试框架。
  • 这是一个完整的重写,但可以在相同的代码库中与较旧的JUnit版本共存

一个专门的开发人员小组目前正在研究JUnit 5,这是Java最受欢迎的库之一的下一个版本。 尽管表面上的改进是有意进行的,但真正的创新是在后台进行的,有可能重新定义JVM上的测试。

在2015年11月发布原型并在2016年2月发布 Alpha版本之后,7月发布Milestone 1Milestone 2 。 我们在这里试驾最新,最出色的产品!

在第一部分中,我们将了解如何开始编写测试,了解新版本带来的所有小改进,讨论JUnit团队为何决定是时候进行重写了,最重要的是,看看如何新的体系结构可以改变游戏规则,从而可以在JVM上进行测试。

第二部分将更详细地介绍如何运行测试,展示JUnit的一些很酷的新功能以及如何扩展核心功能。

写作测试

让我们从媒体资源开始,看看如何快速完成一些测试。

五秒钟设置

稍后,我们将更详细地介绍设置和体系结构。 现在,我们只需使用我们选择的构建工具导入这些工件:

org.junit.jupiter:junit-jupiter-api:5.0.0-M2
org.junit.jupiter:junit-jupiter-engine:5.0.0-M2
org.junit.platform:junit-platform-runner:1.0.0-M2

今天是今天 一旦全面支持JUnit 5,我们将只需要junit-jupiter-api工件。 (稍后会详细介绍。)

接下来,我们需要一个类来包含我们的测试:

package com.infoq.junit5;

import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
public class JUnit5Test {
    // tests go here
}

现在我们都准备编写JUnit 5测试,并让我们的IDE或构建工具执行它们。

一个简单的例子

对于简单的测试,变化不大:

@RunWith(JUnitPlatform.class)
public class JUnit5Test {

    @BeforeAll
    static void initializeExternalResources() {
   	 System.out.println("Initializing external resources...");
    }

    @BeforeEach
    void initializeMockObjects() {
   	 System.out.println("Initializing mock objects...");
    }

    @Test
    void someTest() {
   	 System.out.println("Running some test...");
   	 assertTrue(true);
    }

    @Test
    void otherTest() {
   	 assumeTrue(true);

   	 System.out.println("Running another test...");
   	 assertNotEquals(1, 42, "Why wouldn't these be the same?");
    }

    @Test
    @Disabled
    void disabledTest() {
   	 System.exit(1);
    }

    @AfterEach
    void tearDown() {
   	 System.out.println("Tearing down...");
    }

    @AfterAll
    static void freeExternalResources() {
   	 System.out.println("Freeing external resources...");
    }

}

从表面上看,JUnit 5故意进行了增量改进。 更具革命性的功能被隐藏起来,我们将在稍后讨论这些功能。 但是首先让我们看一下此版本中一些更明显的改进。

改进措施

能见度

最明显的变化是测试方法不再必须公开。 包可见性足以满足要求(但私有可见性则无法满足),因此我们可以使测试类免受大量公共关键字的困扰。

从理论上讲,测试类也可以具有默认可见性。 但是由于我们只是做了简单的设置,我们的工具将仅扫描公共类以查找注释。 一旦获得JUnit 5支持,这种情况就会改变。

测试生命周期

@测试

最基本的JUnit 4批注是@Test ,用于标记要作为测试运行的方法。

注释实际上不再改变,尽管它不再使用可选参数。 现在可以通过断言来验证预期的异常。 (对于超时,尚无替代品。)

之前和之后

要运行代码来设置和拆除我们的测试,我们可以使用@BeforeAll@BeforeEach@AfterEach@AfterAll 。 他们更恰当地命名,但语义上JUnit 4的@BeforeClass,@Before,@After@AfterClass。

因为为每个测试都创建了一个新实例,并且@BeforeAll / @AfterAll仅对所有它们调用一次,所以尚不清楚应该在哪个实例上调用它们,因此它们必须是静态的(就像@BeforeClass那样)JAfter 4中的@AfterClass )。

如果使用相同的注释对不同的方法进行注释,则故意未定义执行顺序。

禁用测试

可以使用@Disabled禁用测试,这等效于JUnit 4的@Ignored 。 这只是Condition的特例,稍后我们将在讨论扩展JUnit时看到。

断言

一切设置完成并执行后,最终取决于声明来验证所需的行为。 在这方面也有许多渐进式的改进:

  • 断言消息现在位于参数列表的最后。 这使得带有和不带有消息的调用更加统一,因为前两个参数始终是期望值和实际值,而可选参数位于最后。
  • 使用lambda,可以延迟创建断言消息,如果创建是一个冗长的操作,则可以提高性能。
  • 布尔断言接受谓词。

然后是新的assertAll ,它检查一组相关的调用结果,如果断言失败,则不会短路,但会为所有结果打印值:

@Test
void assertRelatedProperties() {
    Developer dev = new Developer("Johannes", "Link");

    assertAll("developer",
   		 () -> assertEquals("Marc", dev.firstName()),
   		 () -> assertEquals("Philipp", dev.lastName())
    );
}

这将产生以下失败消息:

org.opentest4j.MultipleFailuresError: developer (2 failures)
    expected: <Marc> but was: <Johannes>
    expected: <Philipp> but was: <Link>

请注意,即使名字已经使断言失败,打印输出也将如何包括姓氏失败。

最后,我们有assertThrowsExpectThrows ,如果指定的异常没有被调用的方法抛出,则两者都将通过测试。 但是要进一步断言异常的属性(例如,消息包含某些信息), expectThrows将其返回。

@Test
void assertExceptions() {
    // assert that the method under test
    // throws the expected exception */
    assertThrows(Exception.class, unitUnderTest::methodUnderTest);

    Exception exception = expectThrows(
        Exception.class,
        unitUnderTest::methodUnderTest);
    assertEquals("This shouldn't happen.", exception.getMessage());
}

假设条件

假设可以在某些条件符合预期的情况下运行测试。 假设必须用布尔表达式表达,如果不满足条件,则测试退出。 这可以用来减少测试套件的运行时间和冗长,尤其是在失败的情况下。

@Test
void exitIfFalseIsTrue() {
    assumeTrue(false);
    System.exit(1);
}

@Test
void exitIfTrueIsFalse() {
    assumeFalse(this::truism);
    System.exit(1);
}

private boolean truism() {
    return true;
}

@Test
void exitIfNullEqualsString() {
    assumingThat(
             // state an assumption (a false one in this case) ...
   		 "null".equals(null),
             // … and only execute the lambda if it is true
   		 () -> System.exit(1)
    );
}

假设可以用于中止不满足先决条件的测试( 假定为True假定为False ),或者用于在条件成立时执行测试的特定部分( assumimgThat) 。 主要区别在于,中止的测试报告为已禁用,而由于条件不成立而为空的测试显示为绿色。

一点历史

如我们所见,JUnit 5对我们编写测试的方式进行了许多增量改进。 当然,它也为表带来了新功能,我们将在后面介绍。 但是,有趣的是,投入大量精力到新版本中的真正原因要更深一些。

为什么要重写JUnit?

JUnit和工具

随着诸如测试驱动的开发和持续集成之类的开发技术变得越来越普及,对于开发人员的日常业务而言,测试变得越来越重要。 因此,对IDE的需求也在增长。 开发人员想要简单而具体的执行(仅限于单个方法),快速反馈和轻松导航。 构建工具和CI服务器添加了自己的要求。

JUnit 4如何为此做好准备? 事实证明,这并不顺利。 除了它唯一的依赖关系Hamcrest 1.3和JUnit 4.12,JUnit 4.12是一个整体工件,其中包含API开发人员针对它们编写测试并运行这些测试的引擎。 例如,发现测试必须由每个想要这样做的工具再次实施。

不幸的是,这还不足以支持某些高级工具功能。 工具开发人员经常不得不通过使用反射来即兴访问JUnit的内部API,非公共类甚至私有字段。 突然之间,可以免费重构的实现细节实际上已成为公共API的一部分。 所导致的锁定使维护变得不愉快,并且难以进一步改进。

当前重写的发起人约翰尼斯·林克(Johannes Link) 称其为“西西弗斗争”,并总结了如下情况:

JUnit作为平台的成功阻碍了JUnit作为工具的开发。

扩展JUnit

测试运行程序是扩展JUnit 4的原始机制。我们可以创建自己的运行程序实现,并通过使用@RunWith(OurNewRunner.class)注释测试类来告诉JUnit使用它。 我们的自定义运行程序必须实现完整的测试生命周期,包括实例化,设置和拆卸,运行测试,处理异常,发送通知等。

这使得它相当笨重,并且不便于创建小的扩展。 而且它有一个严格的限制,即每个测试班级只能有一个跑步者,这使得无法将它们组合在一起并无法同时受益于Mockito和Spring跑步者的功能。

为了减轻这些限制,JUnit 4.7引入了rules 。 默认的JUnit 4运行器会将测试包装为Statement ,并将其传递给应用于该测试的规则。 然后,他们可以执行一些操作,例如创建一个临时文件夹在Swing的事件分发线程中运行测试,或者如果测试运行时间过长则让测试超时

规则是一个很大的改进,但通常仅限于在测试运行之前,之中和之后执行一些代码。 但是在这些生命周期点之外,几乎没有支持来实现要求更高的扩展。

然后是一个事实,所有测试用例在开始执行之前都必须知道。 这会阻止动态创建测试用例,例如响应测试执行过程中观察到的行为。

现在有两种相互竞争的扩展机制,每种机制都有其自身的局限性,但也有很多重叠之处。 这使得干净扩展很困难。 此外,据报道,组成不同的扩展名是有问题的,并且通常不会达到预期的效果。

Lambda的呼唤

JUnit 4已有10多年的历史了,仍然使用Java 5,因此它错过了所有后续Java语言改进,尤其是lambda表达式,该表达式允许以下结构:

test(“someTest”, () -> {
   	 System.out.println("Running some test...");
   	 assertTrue(true);
    });

输入JUnit Lambda

因此,我们现在了解了导致重写想法的更广阔的前景,JUnit Lambda团队在2015年围绕这个目标成立了。 其核心是Jo hannes Link 后来离开了该项目 Marc PhilippStefan BechtoldMatthias MerdesSam Brannen

赞助和众筹

有趣的是,马克·菲利普(Marc Philipp),斯特凡· 贝希特 (Stefan Bechtold)和马蒂亚斯·梅德斯(Matthias Merdes)的雇主Andrena ObjectsNamicsHeidelberg Mobil分别 慷慨地赞助了该项目六个星期的专职工作。 但是很明显,组织一次研讨会需要更多的开发时间和更多的资金。 他们估计他们至少需要25,000欧元,并为此目标在Indiegogo上发起众筹活动 。 在开始有些缓慢之后,事情最终加速了,并导致了高达53,937欧元(约合60,000美元)的收入。

这使团队可以在该项目上花费大约两个月的全职开发时间。 顺便说一下,这些资金的使用是完全透明的

原型,Alpha版本和里程碑

2015年10月,JUnit Lambda团队在德国卡尔斯鲁厄举行了一个研讨会 ,然后开始了一个月的全职工作。 最终的原型在四个星期后发布,展示了许多新功能,甚至还包括一些未纳入当前版本的实验性功能。

在收集反馈后,团队开始着手开发下一个版本,将其更名为JUnit 5,并于2016年2月发布了alpha版本。另一轮反馈和五个月的紧张开发工作随后,里程碑1发布于2016年7月7日。两周后来又进行了一些错误修复,我们当前的主题Milestone 2有了新的发展。 在6月,该项目进行了另一次转换,并且JUnit 5被分为JUnit Jupiter,JUnit Platform和JUnit Vintage,这是我们接下来将讨论的区别。

反馈

随着新版本的问世,该项目再次收集了反馈。敦促社区尝试JUnit 5,并在GitHub上公开问题和请求请求。 我们应该抓住这个机会,权衡一下!

团队和一些早期采用者也开始谈论JUnit 5 。 接下来的是:

下一个里程碑和最终版本

几个月前,团队花了群众的钱,回到了日常工作,在空闲时间从事JUnit 5的工作。 他们正在取得良好的进步! Milestone 3的开发工作已经在进行中,计划于今年晚些时候发布。 谁知道呢,也许它已经是最终版本了。

建筑

我们已经看到了JUnit 4的整体架构如何使开发变得困难。

那么新版本将如何改变呢?

分离问题

测试框架有两个重要任务:

  • 使开发人员能够编写测试
  • 使工具能够运行测试

当仔细考虑第二点时,很明显它包含的部分在不同的测试框架中是相同的。 无论是JUnit,TestNg,Spock,Cucumber,ScalaTest等,工具通常都需要测试的名称和结果,执行测试的方式,对报告层次结构感兴趣等。

为什么要重复在不同框架中处理这些问题的代码? 如果在抽象级别上功能始终相同,为什么需要工具来实现对此框架或版本(和版本)的特定支持?

JUnit作为平台

JUnit可能是最常用的Java库 ,并且肯定是JVM上最流行的测试框架。 与IDE和构建工具的紧密集成紧密结合在一起。

同时,其他测试框架正在探索有趣的新测试方法,尽管缺乏集成通常会使开发人员重新使用JUnit。 也许他们可以从JUnit的成功中受益并piggy带在为其提供的集成上? (就像许多语言一样,利用JVM可以从Java的成功中受益。)

移民

但这不仅是理论上的争论; 这对于JUnit项目本身很重要,因为它与迁移的关键问题联系在一起。 现有的甚至新的工具是否应该同时支持版本4和5? 可能很难说服工具供应商添加这么多代码,但是如果他们不这样做,开发人员将没有动力升级他们的测试框架。

如果JUnit 5可以在统一的API之后运行两个版本的测试,那显然会更强大,更方便,从而允许工具删除过时的JUnit 4集成。

模块化

这些想法导致了架构的脱钩,其中不同的角色(开发人员,运行时,工具)依赖于不同的工件:

  1. 开发人员针对其编写测试的API
  2. 每个API的引擎,以发现,呈现和运行相应的测试
  3. 所有引擎都必须实现的API,以便可以统一使用它们
  4. 协调引擎的机制

这将“ JUnit工具”(1.和2)与“ JUnit平台”(3.和4)分开。 为了使区别更加清晰,项目选择为其赋予一个命名模式:

  • 我们上面已经看到(并将在下一篇文章中继续讨论)的新API称为JUnit Jupiter 。 这是我们开发人员与之联系最多的。
  • 工具平台将适当地称为JUnit Platform
  • 我们还没有看到它,但是还有一个JUnit Vintage子项目,它将适应由JUnit 5运行的JUnit 3和4测试。

JUnit 5是这三个部分的总和。 其新的体系结构就是这种区别的结果:

junit-jupiter-api (1)

开发人员用来编写测试的API。 包含我们之前看到的注释,断言等。

junit-jupiter-engine (2)

运行JUnit 5测试(即针对junit-jupiter-api编写的测试)的junit-engine-api的实现 (请参见下文)。

junit-platform-engine (3)

所有测试引擎都必须实现API,因此可以以统一的方式访问它们。 引擎可以运行典型的JUnit测试,也可以选择运行用TestNGSpockCucumber等编写的测试。通过向Java的ServiceLoader注册自身,引擎可以使它们对启动器可用(见下文)。

junit-platform-launcher (4)

使用ServiceLoader发现测试引擎的实现并协调其执行。 它为IDE提供了API,并提供了与测试执行进行交互的工具,例如,通过启动单个测试并显示其结果。

这种架构的好处显而易见。 我们只需要另外两个组件即可使其执行JUnit 4测试:

junit-4.12 (1)

JUnit 4构件充当开发人员针对其实施测试的API,但还包含如何运行测试的主要功能。

junit-vintage-engine (2)

junit-platform-engine的实现 ,运行用JUnit 4编写的测试。可以将其视为版本5的JUnit 4的适配器。

其他框架已经提供了用于编写测试的API,因此完全JUnit 5集成所缺少的只是测试引擎实现。

一张图片胜过千言万语:

API生命周期

下一个要解决的问题是每个人都在使用所有这些内部API。 因此,团队为其API创建了生命周期。 就在这里,直接从进行解释:

内部

除JUnit本身外,不得用于任何其他代码。 可能被删除,恕不另行通知。

不推荐使用

如果不再使用,则可能会在下一个次要版本中消失。

实验性

旨在提供我们希望获得反馈的实验性新功能。 谨慎使用,将来可能会升级为“保持”或“稳定”,但也可能会被删除,恕不另行通知。

保持

适用于至少在当前主版本的下一个次要版本中不会以向后不兼容的方式更改的功能。 如果计划删除,它将首先降级为“已弃用”。

稳定

适用于在当前主要版本中不会以向后不兼容的方式更改的功能。

公开显示的类将与@API(使用),其中使用在上述列表中,例如@API(稳定)的值中的一个来注释。 预期该计划将使API调用者更好地了解他们正在进入的领域,并且JUnit团队可以自由地无情地更改或删除不受支持的API。

开放测试联盟

如我们所见,JUnit 5体系结构使IDE和构建工具能够将其用作其他测试框架的基础(假设这些框架提供了相应的引擎)。 使用这种方法,工具可以统一发现,执行和评估测试,而无需实施特定于框架的支持。

还是可以?

测试失败通常表示为异常。 不幸的是,不同的测试框架和断言库通常不使用相同的类,而是实现自己的变体(通常是扩展AssertionErrorRuntimeException )。 这使得互操作性比必要的更为复杂,并阻止了工具的统一处理。

为了解决此问题,JUnit Lambda团队拆分了一个单独的项目, 即JVM开放测试联盟 。 这是他们的建议:

基于最近与IDE以及Eclipse,Gradle和IntelliJ的构建工具开发人员的讨论,JUnit Lambda团队正在研究一个开源项目的提案,以为在JVM上测试库提供最小的通用基础。

该项目的主要目标是使测试框架(如JUnit,TestNG,Spock等)和第三方断言库(如Hamcrest,AssertJ等)能够使用IDE和构建工具可以一致支持的一组通用异常。所有测试场景中的方式–例如,用于一致处理失败的断言和失败的假设以及在IDE和报告中可视化测试执行。

到现在为止,这些项目几乎都没有得到回应。 亲爱的读者,如果您认为这是个好主意,建议您向您选择的框架的维护者指出开放测试联盟。

兼容性

鉴于JUnit可以同时运行版本4 版本5的测试引擎,看起来项目可以同时包含两个版本的测试。 实际上,JUnit 5占用了新的名称空间: org.junit.jupiterorg.junit.platform org.junit.vintage 。 这意味着当并行使用不同的JUnit版本时不会有冲突,并且这允许缓慢迁移到JUnit 5。

通过异常与JUnit通信的测试库(例如HamcrestAssertJ)将继续在新版本中工作。

摘要

至此,我们结束了JUnit 5测试驱动器的第一部分。 我们已经建立了一个编写和运行测试的环境,并了解了API表面是如何进行逐步演变的。 有了这个,您就可以开始尝试!

我们还讨论了如何将工具绑定到JUnit 4实现细节,以至于需要重新开始。 但是,这不是唯一的原因。 JUnit 4的令人满意的扩展模型以及使用lambda表达式定义测试的愿望也导致了重写。

新架构旨在避免过去的错误。 它分为JUnit Jupiter (我们用于编写测试的库)和JUnit Platform (可以针对其构建的平台工具),将这两个方面明确分开。 而且,这也将“ JUnit the platform”的成功推向了现在可以与其集成的其他测试框架。

在第二部分中 ,我们将仔细研究如何在IDE,构建工具甚至控制台中运行JUnit 5测试。 最后,我们将看到新版本带来了几个很酷的新功能。 扩展模型非常值得期待...

翻译自: https://www.infoq.com/articles/JUnit-5-Early-Test-Drive/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

junit数据驱动测试

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值