参数化测试 junit_JUnit 5 –参数化测试

参数化测试 junit

JUnit 5令人印象深刻,尤其是当您深入研究扩展模型和体系结构时 。 但是从表面上讲,编写测试的地方,开发的过程比革命的过程更具进化性 – JUnit 4上没有杀手级功能吗? 幸运的是,至少有一个:参数化测试。 JUnit 5具有参数化测试方法的本机支持,以及一个扩展点,该扩展点允许使用同一主题的第三方变体。 在本文中,我们将研究如何编写参数化测试-创建扩展将留待将来使用。

总览

这篇文章是有关JUnit 5的系列文章的一部分:

  • 建立
  • 基本
  • 建筑
  • 移民
  • 动态测试
  • 参数化测试
  • 扩展模型
  • 条件
  • 参数注入

本系列基于预发布版本Milestone 4,并且在发布新的里程碑或GA版本时会进行更新。 另一个很好的来源是《 JUnit 5用户指南》 。 您可以在GitHub上找到所有代码示例。

在整个这篇文章中,我将大量使用terms 参数和自变量,并且它们的含义并不相同。 根据维基百科 :

术语参数通常用于指代在函数定义中找到的变量,而参数指代传递的实际输入。

您好,参数化世界

参数化测试入门非常容易,但是在开始乐趣之前,您必须向项目添加以下依赖项:

  • 群组ID :org.junit.jupiter
  • 工件ID :junit-jupiter-params
  • 版本 :5.0.0-M4

然后,通过在@ParameterizedTest而不是@Test上声明带有参数和拍击的测试方法开始:

@ParameterizedTest
// something's missing - where does `word` come from?
void parameterizedTest(String word) {assertNotNull(word);
}

它看起来不完整– JUnit如何知道参数字应采用哪些参数? 好吧,由于您为它定义了零个参数,因此该方法将被执行零次,并且JUnit实际上报告了该方法的Empty测试套件。

为了使事情发生,您需要提供参数,您可以从中选择各种来源。 可以说,最简单的方法是@ValueSource:

@ParameterizedTest
@ValueSource(strings = { "Hello", "JUnit" })
void withValueSource(String word) {assertNotNull(word);
}

确实,现在测试执行了两次:一次是“ Hello”,一次是“ JUnit”。 在IntelliJ中,如下所示:

这就是开始进行参数化测试所需的一切!

对于现实生活,您应该了解@ParamterizedTest的来龙去脉(例如,如何命名它们),其他参数来源(包括如何创建自己的参数)以及到目前为止的更多知识。有点神秘的功能,称为参数转换器。 我们现在将研究所有这些。

参数化测试的来龙去脉

使用@ParameterizedTests创建测试很简单,但是要充分利用该功能,有一些细节是很好的。

测试名称

从上面的IntelliJ屏幕截图可以看出,参数化测试方法显示为带有每次调用的子节点的测试容器。 这些节点的名称默认为“ [[{index}] {arguments}”,但可以使用@ParameterizedTest设置其他名称:

@ParameterizedTest(name = "run #{index} with [{arguments}]")
@ValueSource(strings = { "Hello", "JUnit" })
void withValueSource(String word) { }

只要修剪后的字符串不为空,就可以将其用作测试的名称。 可以使用以下占位符:

  • {index}:从1开始计数测试方法的调用; 该占位符被替换为当前调用的索引
  • {arguments}:被方法的n个参数替换为{0},{1},…{n}(到目前为止,我们仅看到带有一个参数的方法)
  • {i}:被当前调用中第i个参数具有的参数替换

我们将在一分钟内介绍替代资源,因此暂时忽略@CsvSource的详细信息。 只需看看可以通过这种方式构建的出色测试名称,尤其是与@DisplayName一起使用 :

@DisplayName("Roman numeral")
@ParameterizedTest(name = "\"{0}\" should be {1}")
@CsvSource({ "I, 1", "II, 2", "V, 5"})
void withNiceName(String word, int number) {    }

非参数化参数

不管参数化测试如何,JUnit Jupiter都已经可以将参数注入测试方法中 。 只要将每次调用中变化的参数排在首位,这可以与参数化测试结合使用:

@ParameterizedTest
@ValueSource(strings = { "Hello", "JUnit" })
void withOtherParams(String word, TestInfo info, TestReporter reporter) {reporter.publishEntry(info.getDisplayName(), "Word: " + word);
}

与以前一样,此方法被调用两次,两次参数解析器都必须提供TestInfo和TestReporter的实例。 在这种情况下,这些提供程序已内置在Jupiter中,但是自定义提供程序(例如用于模拟)也将同样有效。

元注释

最后但并非最不重要的一点是,@ParameterizedTest(以及所有源代码)可以用作元注释来创建自定义扩展和注释 :

@Params
void testMetaAnnotation(String s) { }@Retention(RetentionPolicy.RUNTIME)
@ParameterizedTest(name = "Elaborate name listing all {arguments}")
@ValueSource(strings = { "Hello", "JUnit" })
@interface Params { }

参数来源

三种成分进行参数化测试:

  1. 有参数的方法
  2. @ParameterizedTest批注
  3. 参数值,即参数

参数由源提供,可以为测试方法使用任意数量的参数,但至少应有一个(否则测试将根本不会执行)。 存在一些特定的资源,但是您也可以自由创建自己的资源。

要理解的核心概念是:

  • 每个来源都必须为所有测试方法参数提供参数(因此第一个参数不能有一个来源,第二个参数不能有另一个来源)
  • 该测试将对每组参数执行一次

价值来源

您已经看到了@ValueSource的实际应用。 它使用起来非常简单,并且可以为几种基本类型输入安全类型。 您只需应用注释,然后从以下元素之一(也可以是其中一个)中进行选择:

  • String [] strings()
  • int [] ints()
  • long [] longs()
  • double [] doubles()

之前,我向您展示了字符串–在这里,您已经花费了很长时间

@ParameterizedTest
@ValueSource(longs = { 42, 63 })
void withValueSource(long number) { }

有两个主要缺点:

  • 由于Java对有效元素类型的限制 ,它不能用于提供任意对象(尽管对此有一种补救方法-请等到您了解参数转换器之后再进行选择 )
  • 它只能用于具有单个参数的测试方法

因此,对于大多数非平凡的用例,您将不得不使用其他来源之一。

枚举来源

这是一个非常具体的资源,您可以使用它对一个枚举或其子集的每个值运行一次测试:

@ParameterizedTest
@EnumSource(TimeUnit.class)
void withAllEnumValues(TimeUnit unit) {// executed once for each time unit
}@ParameterizedTest
@EnumSource(value = TimeUnit.class,names = {"NANOSECONDS", "MICROSECONDS"})
void withSomeEnumValues(TimeUnit unit) {// executed once for TimeUnit.NANOSECONDS// and once for TimeUnit.MICROSECONDS
}

直截了当吧? 但是请注意,@ EnumSource只为一个参数创建参数,这与源必须为每个参数提供参数的事实相结合,这意味着它只能在单参数方法上使用。

方法来源

@ValueSource和@EnumSource非常简单,并且有些局限性–在通用范围的另一端是@MethodSource。 它只是简单地命名将提供参数流的方法。 从字面上看:

@ParameterizedTest
@MethodSource(names = "createWordsWithLength")
void withMethodSource(String word, int length) { }private static Stream createWordsWithLength() {return Stream.of(ObjectArrayArguments.create("Hello", 5),ObjectArrayArguments.create("JUnit 5", 7));
}

Argument是一个包装对象数组的简单接口,ObjectArrayArguments.create(Object…args)从提供给它的varargs创建它的实例。 支持注释的类完成了其余工作,并且withMethodSource这样执行了两次:一次用word =“ Hello” / length = 5,一次用word =“ JUnit 5” / length = 7。

@MethodSource命名的方法必须是静态的,并且可以是私有的。 他们必须返回一种集合,该集合可以是任何Stream(包括原始的特殊化),Iterable,Iterator或数组。

如果源仅用于单个参数,则可能空白返回此类实例而不将其包装在Argument中:

@ParameterizedTest
@MethodSource(names = "createWords")
void withMethodSource(String word) { }private static Stream createWords() {return Stream.of("Hello", "Junit");
}

就像我说的那样,@ MethodSource是Jupiter提供的最通用的资源。 但这会招致声明方法和将参数组合在一起的开销,这对于较简单的情况来说有点多。 最好使用两个CSV来源。

CSV来源

现在,它变得非常有趣。 能够在那时和那里为几个参数定义少数参数集而不必通过声明方法来很好吗? 输入@CsvSource! 使用它,您可以将每次调用的参数声明为以逗号分隔的字符串列表,并将其余参数留给JUnit:

@ParameterizedTest
@CsvSource({ "Hello, 5", "JUnit 5, 7", "'Hello, JUnit 5!', 15" })
void withCsvSource(String word, int length) { }

在此示例中,源标识了三组参数,从而导致了三个测试调用,然后继续将它们分开(以逗号分隔)并将其转换为目标类型。 看到“'Hello,JUnit 5!',15”中的单引号吗? 这是使用逗号的方式,而不会在该位置将字符串切成两半。

将所有参数都表示为字符串会引起一个问题,即如何将它们转换为正确的类型。 我们待会儿会谈,但是在我想快速指出之前,如果您有大量输入数据,可以将它们自由存储在外部文件中:

@ParameterizedTest
@CsvFileSource(resources = "word-lengths.csv")
void withCsvSource(String word, int length) { }

请注意,资源可以接受多个文件名,并将一个接一个地处理它们。 @CsvFileSource的其他元素允许指定文件的编码,行分隔符和定界符。

自定义参数来源

如果JUnit内置的源代码无法满足您的所有用例,则可以自由创建自己的用例。 我将不赘述-足以说明,您必须实现此接口…

public interface ArgumentsProvider {Stream<? extends Arguments> provideArguments(ContainerExtensionContext context) throws Exception;}

…,然后将您的源代码与@ArgumentsSource(MySource.class)或自定义注释一起使用 。 您可以使用扩展上下文访问各种信息,例如,调用源的方法,以便知道它有多少个参数。

现在,开始转换这些参数!

参数转换器

除了方法源之外,参数源提供的类型的种类非常有限:仅字符串,枚举和一些基元。 当然,这不足以编写全面的测试,因此需要一条通往更丰富的类型环境的道路。 参数转换器就是那条路:

@ParameterizedTest
@CsvSource({ "(0/0), 0", "(0/1), 1", "(1/1), 1.414" })
void convertPointNorm(@ConvertPoint Point point, double norm) { }

让我们看看如何到达那里……

首先,一般观察:无论提供的参数和目标参数具有什么类型,都将始终要求转换器将其转换为另一种。 但是,只有前面的示例声明了一个转换器,那么在所有其他情况下会发生什么?

默认转换器

Jupiter提供了一个默认转换器,如果未应用其他转换器,则将使用它。 如果参数和参数类型匹配,则转换为空操作,但如果参数为字符串,则可以将其转换为多种目标类型:

  • char或Character(如果字符串的长度为1)(如果您使用UTF-32字符(如表情符号,因为它们包含两个Java字符),则可能会使您失望)
  • 其他所有原语及其包装类型以及它们各自的valueOf方法
  • 通过使用字符串和目标枚举调用Enum :: valueOf来获取任何枚举
  • 一堆时间类型,例如Instant,LocalDateTime等,OffsetDateTime等,ZonedDateTime,Year和YearMonth及其各自的解析方法

这是一个简单的示例,其中显示了其中一些操作:

@ParameterizedTest
@CsvSource({"true, 3.14159265359, JUNE, 2017, 2017-06-21T22:00:00"})
void testDefaultConverters(boolean b, double d, Summer s, Year y, LocalDateTime dt) { }enum Summer {JUNE, JULY, AUGUST, SEPTEMBER;
}

受支持的类型的列表可能会随着时间的推移而增长,但是很明显,它不能包括特定于您的代码库的类型。 这是定制转换器输入图片的地方。

定制转换器

使用自定义转换器,您可以将源发出的参数(通常是字符串)转换为要在测试中使用的任意类型的实例。 创建它们很容易–您所需要做的就是实现ArgumentConverter接口:

public interface ArgumentConverter {Object convert(Object input, ParameterContext context)throws ArgumentConversionException;}

输入和输出是无类型的,这有点令人讨厌,但是,由于Jupiter都不知道两者的类型,因此在进行更具体的输入方面实在没有用。 您可以使用参数上下文获取有关要为其提供参数的参数的更多信息,例如参数的类型或最终将在其上调用测试方法的实例。

对于已经具有静态工厂方法(例如“(1/0)”)的Point类,convert方法非常简单:

@Override
public Object convert(Object input, ParameterContext parameterContext)throws ArgumentConversionException {if (input instanceof Point)return input;if (input instanceof String)try {return Point.from((String) input);} catch (NumberFormatException ex) {String message = input+ " is no correct string representation of a point.";throw new ArgumentConversionException(message, ex);}throw new ArgumentConversionException(input + " is no valid point");
}

Point的第一个检查输入实例有点麻木(为什么它已经是一个点了?),但是一旦我开始打开类型,便无法使自己忽略这种情况。 随时判断我。

现在,您可以使用@ConvertWith应用转换器:

@ParameterizedTest
@ValueSource(strings = { "(0/0)", "(0/1)","(1/1)" })
void convertPoint(@ConvertWith(PointConverter.class) Point point) { }

或者,您可以创建一个自定义批注以使其看起来不太技术:

@ParameterizedTest
@ValueSource(strings = { "(0/0)", "(0/1)","(1/1)" })
void convertPoint(@ConvertPoint Point point) { }@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@ConvertWith(PointConverter.class)
@interface ConvertPoint { }

这意味着通过使用@ConvertWith或自定义注释对参数进行注释,JUnit Jupiter将传递提供给转换器的源的任何参数。 通常,您会将其应用于发出字符串的@ValueSource或@CsvSource之类的源,以便随后将其解析为您选择的对象。

反射

那是一个很大的旅程,所以让我们确保我们拥有一切:

  • 我们首先添加了junit-jupiter-params工件,然后将@ParameterizedTest应用于带有参数的测试方法。 在研究了如何命名参数化测试之后,我们开始讨论参数的来源。
  • 第一步是使用@ ValueSource,@ MethodSource或@CsvSource之类的源来为方法创建参数组。 每个组都必须具有所有参数的参数(参数解析器中的参数除外),并且每个组将调用该方法一次。 可以实现自定义源并将其与@ArgumentsSource一起应用。
  • 由于源通常仅限于几种基本类型,因此第二步是将它们转换为任意类型。 默认转换器对基元,枚举和某些日期/时间类型执行此操作; 定制转换器可以与@ConvertWith一起应用。

这使您可以轻松地使用JUnit Jupiter参数化您的测试!

但是,这种特定机制很可能无法满足您的所有需求。 在这种情况下,您会很高兴听到它是通过扩展点实现的,可用于创建您自己的参数化测试的变体–我将在以后的文章中对此进行探讨,请继续关注。

翻译自: https://www.javacodegeeks.com/2017/06/junit-5-parameterized-tests.html

参数化测试 junit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/335098.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

devexpress textedit调整文字何文本框的间距_手把手教学:用PPT做效果超赞的文字效果...

本文总计&#xff1a;2391 字预计阅读时间&#xff1a;6 分钟昨天文章的头图&#xff0c;貌似反馈还不错&#xff0c;挺多人比较感兴趣。所以&#xff0c;今天就分享一下&#xff0c;这种文字排版效果&#xff0c;是怎么做出来的。而且今天的实现手法与效果&#xff0c;做了一些…

micropython stm32f030_24C02 读写代码,基于STM32F030

EEPROM可以掉电保存数据&#xff0c;是一种在产品中经常使用的器件&#xff0c;24Cxx 系列更是被广泛采用。下面是的工程在 24LC02 上调试通过&#xff0c;实现了字节读写&#xff0c;和页读写&#xff0c;MCU 是 STM32F030。采用了 MCU 的硬件 I2C 控制器。24LC02硬件连接&…

IntelliJ IDEA for Mac 如何取消双击shift键打开全局搜索弹窗

取消双击shift键打开全局搜索弹窗 按ShiftcmdA&#xff0c;打开如下图的搜索框&#xff1a; 输入Registry搜索后打开如下的窗口&#xff1a; 3. 找到“ide.suppress.double.click.handler”&#xff0c;将后面的复选框勾上&#xff0c;勾选上复选框后直接关闭退出&#xff0c;…

kie-api_7.0上的新KIE持久性API

kie-api这篇文章介绍了即将到来的Drools和jBPM持久性API。 创建持久性api&#xff08;不绑定到JPA&#xff09;的动机是因为Drools和jBPM中的持久性直到7.0.0发行版才允许将替代性持久性机制与JPA完全集成。 尽管JPA是一个出色的api&#xff0c;但它与传统RDBMS模型紧密地结合在…

备抵附加账户的期末余额_备抵账户,附加账户和备抵附加账户的区别,分别有哪些会计科目,举例说明...

备抵账户又叫抵减账户&#xff0c;它是作为被调整对象原始数额的抵减项目&#xff0c;以确定被调整对象实有数额而设置的账户。备抵账户按被调整账户的性质和内容&#xff0c;又可分为资产类备抵账户和权益类备抵账户两类。(&#xff11;)资产类备抵账户①用途&#xff1a;资产…

vb6 判断打印机是否有效_吊打面试官 | 算法之如何判断括号是否有效?

今天要讲的这道题是 bilibili 今年的笔试真题&#xff0c;也是一道关于栈的经典面试题。经过前面文章的学习&#xff0c;我想很多朋友已经看出来了&#xff0c;我接下来要写的是一个关于「算法图解」的系列文章&#xff0c;中间可能会穿插少量的其他类型的文章&#xff0c;但「…

如何理解字符编码

一直有个困惑&#xff0c;为什么计算机系统搞那么多字符编码&#xff0c;就一个Unicode统一天下不就得了&#xff0c;后来看了篇文章&#xff0c;才多少理解一丁点。 英语的国家&#xff0c;只要一个字节就可以表示全部的字符&#xff0c;一个无符合的字节可以表示256个字符&a…

java bean 验证_Java Bean验证基础

java bean 验证这篇文章总结了一些简单&#xff0c;快速的示例&#xff0c;这些示例说明了您想使用Java Beans Validation API&#xff08;JSR 349&#xff0c;JSR 303&#xff09;进行的最常见操作。 记住&#xff0c;Beans Validation独立于Java EE。 尽管它是作为Java EE兼容…

框架下载_25. Scrapy 框架-下载中间件Middleware

1. Spider 下载中间件(Middleware)Spider 中间件(Middleware) 下载器中间件是介入到 Scrapy 的 spider 处理机制的钩子框架&#xff0c;您可以添加代码来处理发送给 Spiders 的 response 及 spider 产生的 item 和 request2. 激活一个下载DOWNLOADER_MIDDLEWARES要激活一个下载…

android activity 显示无焦点_Android面试题集锦之fragemnt

大家可以关注一下小编&#xff0c;小编以后会一直更新Android相关技术资料文章。创建方式静态创建首先我们需要创建一个xml文件&#xff0c;然后创建与之对应的java文件&#xff0c;通过onCreatView()的返回方法进行关联&#xff0c;最后我们需要在Activity中进行配置相关参数即…

IntelliJ IDEA for Mac 在eclipse(MacOS)模式下的快捷键

文章目录Mac键盘符号eclipse(MacOS)模式下的快捷键General 通用Debugging 调试Search/ Replace 查询/替换Editing 编辑Refactoring 重构Navigation 导航Usage Search 使用查询VCS/ Local History 版本控制/本地历史记录Live Templates 动态代码模板Other 官方文档上没有体现Mac…

java终结器_弃用Java的终结器

java终结器JDK-8165641 &#xff08;“ Deprecate Object.finalize”&#xff09;已打开&#xff0c;以“ deprecate Object.finalize&#xff08;&#xff09; ”&#xff0c;因为“ finalizer本质上存在问题&#xff0c;使用finalizer可能会导致性能问题&#xff0c;死锁&…

node 安装_VUE项目迁移之node.js的安装

【摘要】由于公司的项目需要迁移到VUE中去, 所以就用到了node.js, 这里简单整理了一下node.js的安装教程和环境变量的配置【作者】田鋆鹏Node.js 安装教程1. 在node.js的官网下载安装包下载地址1: https://nodejs.org/en/下载地址2: http://nodejs.cn/直接下载.msi的安装包即可…

jsp mysql servlet_JSP+Servlet+JDBC+mysql实现的学生成绩管理系统

本系统基于JSPServletMysql一个基于JSPServletJdbc的学生成绩管理系统。涉及技术少&#xff0c;易于理解&#xff0c;适合JavaWeb初学者学习使用。难度等级&#xff1a;入门技术栈编辑器Eclipse Version: 2019-12 (4.14.0)前端技术基础&#xff1a;htmlcssJavaScript框架&#…

IntelliJ IDEA for Mac 彻底卸载/彻底删除

删除 /Users/liaowenxiong/Library/Logs/目录下所有与 IntelliJ IDEA 相关的文件&#xff1a; cd /Users/liaowenxiong/Library/Logs/ rm -rf *intellij* rm -rf *IntelliJ* rm -rf *JetBrains* rm -rf *jetbrains*删除 /Users/liaowenxiong/Library/Preferences/目录下所有与…

春天猫rtsy_春天重试,因为冬天来了

春天猫rtsy好的&#xff0c;这实际上与冬天无关&#xff0c;众所周知&#xff0c;冬天已经到了 。 它与Spring Retry有关&#xff0c;Spring Retry是一个小的Spring框架库&#xff0c;它使我们可以向应重试的任何任务添加重试功能。 这里有一个很好的教程 &#xff0c;解释了如…

mariadb mysql 配置文件_MariaDB/MySQL配置文件my.cnf解读

MariaDB/MySQL的默认设置性能非常差&#xff0c;仅仅起一个功能测试的作用&#xff0c;不能用在生产环境中&#xff0c;因此要对一些参数进行调整优化。当然&#xff0c;对配置文件各参数的调整需要根据实际环境&#xff0c;不同时期不同数量级的数据进行性能优化。MySQL/Maria…

python字符串合并去重_015day--python集合和字符串

一、集合关系测试交集&#xff1a; 两个都有 .intersettion() 或用 & 符号差集&#xff1a; 列表a有&#xff0c;列表b没有 .difference() 或用 - 符号并集&#xff1a; 两列表合并&#xff0c;去重 .union() 或用 | 符号合并&#xff1a; .update() 会更改数据…

IntelliJ IDEA for Mac如何存取自定义快捷键配置文件

IntelliJ IDEA for Mac自定义的快捷键配置文件在以下的目录中&#xff1a; /Users/liaowenxiong/library/application support/JetBrains/IntelliJIdea2020.3/keymaps

react 事件处理_在React中处理事件

react 事件处理在使用React渲染RESTful服务后&#xff0c;我们创建了简单的UI&#xff0c;用于渲染从RESTful服务获取的员工列表。 作为本文的一部分&#xff0c;我们将扩展同一应用程序以支持添加和删除员工操作。 我们将通过添加/删除员工操作来更新react-app后端api&#x…