参数化测试是定义和运行多个测试用例的好方法,它们之间的唯一区别是数据。在这里,我们看一下JUnit测试常用的三个不同框架。

在编写单元测试时,通常会在测试方法本身中初始化方法输入参数和预期结果。在某些情况下,使用少量输入就足够了;但是,在某些情况下,我们需要使用大量的值来验证代码中的所有功能。参数化测试是定义和运行多个测试用例的好方法,它们之间的唯一区别是数据。他们可以验证各种值的代码行为,包括边界情况。参数化测试可以增加代码覆盖率,并确保代码按预期运行。

有许多用于Java的良好参数化框架。在本文中,我们将研究JUnit测试常用的三个不同框架,并将它们与每个测试的结构示例进行比较。最后,我们将探索如何简化和加速参数化测试的创建。

JUnit参数化测试框架

让我们比较一下三种最常见的框架:JUnit 4,JunitParams和JUnit5。每个JUnit参数化框架都有自己的优点和缺点。

JUnit 4

优点:

  • 这是JUnit 4内置的参数化框架,因此不需要其他外部依赖项。
  • 它支持Java的较早版本(JDK 7和更早版本)。

缺点:

  • 测试类使用字段和构造函数来定义参数,这会使测试更加冗长。
  • 对于每个要测试的方法,它都需要一个单独的测试类。

JunitParams

优点:

  • 通过允许将参数直接传递给测试方法来简化参数语法。
  • 每个测试类允许多个测试方法(每个方法都有自己的数据)。
  • 支持CSV数据源以及基于注释的值(无需方法)。

缺点:

  • 要求使用JunitParams依赖项配置项目。
  • 在运行和调试测试时,必须运行该类中的所有测试-无法在测试类中运行单个测试方法。

JUnit 5

优点:

  • 该参数化框架内置于JUnit 5中,并改进了JUnit 4中包含的内容。
  • 具有简化的参数语法,例如JunitParams。
  • 支持多种数据集源类型,包括CSV和注释(无需方法)。
  • 即使不需要额外的依赖项,也需要多个.jar。

缺点:

  • 需要Java 8和更高版本的构建系统(4.6版或Maven Surefire 2.21版)。
  • 您的IDE可能还不支持(在撰写本文时,仅Eclipse和IntelliJ支持JUnit 5)。

例子

例如,假设我们有一种处理银行贷款请求的方法。我们可能会编写一个单元测试,以指定贷款请求金额、预付定金金额和其他值。然后,我们将创建断言来验证响应——贷款可以被批准或拒绝,并且响应可以指定贷款的条款。

例如:

<span style="color:#ffffff">public LoanResponse requestLoan(float loanAmount, float downPayment, float availableFunds)
{
LoanResponse response = new LoanResponse();
response.setApproved(true);
if (availableFunds < downPayment) {
response.setApproved(false);
response.setMessage(“error.insufficient.funds.for.down.payment“);
return response;
}
if (downPayment / loanAmount < 0.1) {
response.setApproved(false);
response.setMessage(“error.insufficient.down.payment“);
}
return response;
}</span>

首先,让我们看一下上述方法的常规测试:

<span style="color:#ffffff">@Test
public void testRequestLoan() throws Throwable
{
// Given
LoanProcessor underTest = new LoanProcessor();
// When
LoanResponse result = underTest.requestLoan(1000f, 200f, 250f);
// Then
assertNotNull(result);
assertTrue(result.isApproved());
assertNull(result.getMessage());
}</span>

在此示例中,我们通过请求1000美元的贷款,200美元的首付并指示请求者有250美元的可用资金来测试我们的方法。然后,测试将验证贷款是否已获批准,并且未在响应中提供任何信息。

为了确保对我们的requestLoan()方法进行了彻底的测试,我们需要使用各种预付款,请求的贷款金额和可用资金进行测试。例如,让我们测试一笔零首付的100万美元贷款请求,该请求应被拒绝。我们可以简单地用不同的值复制现有测试,但是由于测试逻辑相同,因此参数化测试效率更高。

我们将参数化请求的贷款金额,预付款和可用资金以及预期的结果:贷款是否被批准,以及在验证后返回的消息。每组请求数据及其预期结果将成为其自己的测试用例。

使用JUnit 4参数化的示例参数化测试

让我们从一个Junit 4 Parameterized示例开始。要创建参数化测试,我们首先需要定义测试的变量。我们还需要包括一个构造函数来初始化它们:

<span style="color:#ffffff">@RunWith(Parameterized.class)
public class LoanProcessorParameterizedTest {
float loanAmount;
float downPayment;
float availableFunds;
boolean expectApproved;
String expectedMessage;
public LoanProcessorParameterizedTest(float loanAmount, float downPayment,
float availableFunds, boolean expectApproved, String expectedMessage)
{
this.loanAmount = loanAmount;
this.downPayment = downPayment;
this.availableFunds = availableFunds;
this.expectApproved = expectApproved;
this.expectedMessage = expectedMessage;
}
// …
}</span>

在这里,我们看到该测试使用@RunWith批注指定该测试将与Junit4参数化运行器一起运行。 该跑步者知道正在寻找一种方法,该方法将为测试提供值集(用@Parameters注释),正确初始化测试并运行多行测试。

请注意,每个参数都在测试类中定义为一个字段,并且构造函数初始化这些值(如果您不想创建构造函数,也可以使用@Parameter注释将值注入字段)。对于值集中的每一行,参数化运行器将实例化测试类并运行该类中的每个测试。

让我们添加一个为参数化运行器提供参数的方法:

<span style="color:#ffffff">@Parameters(name = “Run {index}: loanAmount={0}, downPayment={1}, availableFunds={2}, expectApproved={3}, expectedMessage={4}“)
public static Iterable<Object[]> data() throws Throwable
{
return Arrays.asList(new Object[][] {
{ 1000.0f, 200.0f, 250.0f, true, null }
});
}</span>

值集通过data()方法构建为“对象数组列表”,并使用@Parameters进行注释。请注意,@ Parameters使用占位符设置测试的名称,将在运行测试时将其替换。稍后我们将看到,这使得查看测试结果中的值更加容易。当前,只有一行数据用于测试应批准贷款的情况。我们可以添加更多行以增加被测方法的覆盖范围。

<span style="color:#ffffff">@Parameters(name = “Run {index}: loanAmount={0}, downPayment={1}, availableFunds={2}, expectApproved={3}, expectedMessage={4}“)
public static Iterable<Object[]> data() throws Throwable
{
return Arrays.asList(new Object[][] {
{ 1000.0f, 200.0f, 250.0f, true, null },
{ 1000.0f, 50.0f, 250.0f, false, “error.insufficient.down.payment“ },
{ 1000.0f, 200.0f, 150.0f, false, “error.insufficient.funds.for.down.payment“ }
});
}</span>

在这里,我们有一个测试案例,其中贷款将被批准,而另外两个案例中,由于不同的原因而不应被批准。我们可能要添加使用零或负值的行以及测试边界条件。

现在我们准备创建测试方法:

<span style="color:#ffffff">@Test
public void testRequestLoan() throws Throwable
{
// Given
LoanProcessor underTest = new LoanProcessor();
// When
LoanResponse result = underTest.requestLoan(loanAmount, downPayment, availableFunds);
// Then
assertNotNull(result);
assertEquals(expectApproved, result.isApproved());
assertEquals(expectedMessage, result.getMessage());
}</span>

在这里,我们在调用requestLoan()方法并验证结果时引用这些字段。

JunitParams示例

JunitParams库通过允许将参数直接传递给测试方法来简化参数化测试语法。参数值由单独的方法提供,其名称在@Parameters批注中引用。

<span style="color:#ffffff">@RunWith(JUnitParamsRunner.class)
public class LoanProcessorParameterizedTest2 {
@Test
@Parameters(method = “testRequestLoan_Parameters“)
public void testRequestLoan(float loanAmount, float downPayment, float availableFunds,
boolean expectApproved, String expectedMessage) throws Throwable
{
…
}
@SuppressWarnings(“unused“)
private static Object[][] testRequestLoan_Parameters() throws Throwable {
// Parameters: loanAmount={0}, downPayment={1}, availableFunds={2}, expectApproved={3}, expectedMessage={4}
return new Object[][] {
{ 1000.0f, 200.0f, 250.0f, true, null },
{ 1000.0f, 50.0f, 250.0f, false, “error.insufficient.down.payment“},
{ 1000.0f, 200.0f, 150.0f, false, “error.insufficient.funds.for.down.payment“ }
};
}
}</span>

JunitParams的另一个好处是,除了在代码中提供值外,它还支持使用CSV文件提供值。这允许将测试与数据分离,并在不更新代码的情况下更新数据值。

Junit 5示例

JUnit 5解决了JUnit 4的一些局限和缺点。与JunitParams一样,Junit 5也简化了参数化测试的语法。语法上最重要的变化是:

  • 测试方法使用@ParameterizedTest而不是@Test进行注释
  • 测试方法直接接受参数,而不是使用字段和构造函数
  • 不再需要@RunWith批注

在Junit 5中定义相同的示例如下所示:

<span style="color:#ffffff">public class LoanProcessorParameterizedTest {
@ParameterizedTest(name=“Run {index}: loanAmount={0}, downPayment={1}, availableFunds={2}, expectApproved={3}, expectedMessage={4}“)
@MethodSource(“testRequestLoan_Parameters“)
public void testRequestLoan(float loanAmount, float downPayment, float availableFunds,
boolean expectApproved, String expectedMessage) throws Throwable
{
…
}
static Stream<Arguments> testRequestLoan_Parameters() throws Throwable {
return Stream.of(
Arguments.of(1000.0f, 200.0f, 250.0f, true, null),
Arguments.of(1000.0f, 50.0f, 250.0f, false, “error.insufficient.down.payment“),
Arguments.of(1000.0f, 200.0f, 150.0f, false, “error.insufficient.funds.for.down.payment“)
);
}
}</span>

高效地创建参数化测试

可以想象,编写上面的参数化测试可能会有些麻烦。对于每个参数化的测试框架,都需要正确编写一些样板代码。记住正确的结构可能很困难,而且要花时间写出来。为了使此操作更容易,您可以使用Parasoft Jtest自动生成参数化测试,就像上面描述的那样。为此,只需选择要为其生成测试的方法(在Eclipse或IntelliJ中),即可:

使用默认值和断言生成测试。然后,您可以使用实际输入值和断言配置测试,并将更多数据行添加到data()方法。

运行参数化测试

Parasoft Jtest可以直接在Eclipse和IntelliJ中运行参数化测试。

Eclipse中的JUnit视图

请注意,如图所示,每个测试的名称都包含来自数据集的输入值和预期结果值。由于在每种情况下都会显示输入参数和预期的输出,因此可以使测试失败时的调试更加容易。

您还可以使用Parasoft Jtest的“全部运行”操作:

Parasoft Jtest中的“流树”视图

它分析测试流程并提供有关先前测试运行的详细信息。 这使您可以查看测试中发生的情况,而无需使用断点或调试语句重新运行测试。例如,您可以在“变量”视图中看到参数化的值:

Parasoft Jtest中的变量视图

结论

我们审查的三个框架中的每一个都是不错的选择,并且运作良好。如果使用JUnit 4,则由于测试类的设计更加简洁,并且能够在同一类中定义多个测试方法,因此我倾向于使用JunitParams而不是内置的JUnit 4 Parameterized框架。但是,如果使用JUnit 5,我建议您使用内置的JUnit 5框架,因为它可以解决JUnit 4中的缺点,并且不需要额外的库。我还喜欢使用Parasoft Jtest的单元测试功能来提高参数化测试的创建、执行和调试的效率。

如果您想要亲自尝试,可以获取正版免费试用>>

三种最常见的框架解析 | 如何创建JUnit参数化测试相关推荐

  1. php js对话框,JavaScript_js弹出框、对话框、提示框、弹窗实现方法总结(推荐),一、JS的三种最常见的对话框- phpStudy...

    js弹出框.对话框.提示框.弹窗实现方法总结(推荐) 一.JS的三种最常见的对话框 //====================== JS最常用三种弹出对话框 ==================== ...

  2. 安川伺服总线通讯方式_plc通讯方式有哪三种?plc常见的三种通讯方式

    原标题:plc通讯方式有哪三种?plc常见的三种通讯方式 PLC = Programmable Logic Controller,可编程逻辑控制器,一种数字运算操作的电子系统,专为在工业环境应用而设计 ...

  3. 在修路的时候或者建筑工地,为什么要有人支着一个三脚架测量,其实三脚架上面还有仪器的,通常是四种:水准仪、经纬仪、全站仪、GPS。(前三种较常见)可以理解为分别测:高度差、角度、距离加角度、地理坐标

    我是学这个的,学过一年,专业是土地资源管理. 其实三脚架上面还有仪器的,通常是四种:水准仪.经纬仪.全站仪.GPS.(前三种较常见) 可以理解为分别测:高度差.角度.距离加角度.地理坐标. 只有实际测 ...

  4. 三种形式能量最值解析

    前言 这里有这学期学过的三种模型,需要认真掌握能量的形式,何时最大. 其他大物文章见: 大物文章汇总 简谐振动 但物体运动到位移最大的地方(如图中A点),动能为0,势能最大: 而运动到平衡位置时(如图 ...

  5. java集合框架支持三种类型,Java集合框架(一)

    集合类存放于java.util包中,集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用(reference) 集合类型主要有3种:set(集).lis ...

  6. HTMLDOM中三种元素节点、属性节点、文本节点的测试案例

    HTML dom中常用的三种节点分别是元素节点.属性节点.文本节点. 具体指的内容可参考下图: 以下为测试用例: <!DOCTYPE html> <html><head& ...

  7. npn三种波形失真_【鼎阳硬件智库原创 | 测试测量】初识任意波形发生器

    方浩 鼎阳硬件设计与测试智库专家组成员 在产品调试的过程中,大多数的电路需要输入某种幅度随时间变化的信号,在这样的应用场景中,一个完整的测试测量系统一般会包含激励源,被测件和采集仪器三个部分.采集仪器 ...

  8. aop框架 php,xaop: 支持三种模式的AOP框架,弥补PHPer的不足,并且自带了文档的解析类库,可以一并使用,性能极好,欢迎 STAR 与 FORK。...

    Xaop PHP高性能的AOP扩展 功能特色 基于对象的文档注解AOP模式 方法注入AOP模式 属性AOP模式 系统指令及其含义 xaop.method_prefix AOP文档注解需要排除的方法前缀 ...

  9. 实现二维码-完整三种编码流程加代码解析(javascript)

    效果 输入内容:XXXwedewed生日//&sss乐❤XXXwedewed生日//&sss乐❤ 完整的演示效果为,输入内容后会将解码绘制的每一步都展示(有点长就不全截图了,可以直接移 ...

  10. Python入门小项目-计算阶乘n的三种方法+常见练习(含代码示例)

    今天的文章主要给各位整理了Python常见的集中计算练习,这些也是作为初学者必会的联系项目了,代码分享出来,需要的朋友们可以看下~ 一.计算阶乘n! 整数的阶乘(英语:factorial)是所有小于及 ...

最新文章

  1. 网络工程原理与实践教程实验安排
  2. C++中const用于函数重载
  3. java判断回文数代码实例
  4. 把准脉搏 U-Mail邮件系统2014开足马力
  5. 电话光端机与PCM复用设备的区别
  6. Oracle Golden Gate 系列十三 -- 配置GG进程检查点(checkpoint) 说明
  7. LeetCode 215. 数组中的第K个最大元素(快速排序)
  8. webbrowser 访问iframe拒绝访问_Win10系统下Documents and Settings系统文件夹拒绝访问解决方法...
  9. EasyDarwin添加自定义的服务模块EasyMyModule
  10. ubuntu安装sasl失败 - 解决方法
  11. ubuntu删除了python恢复_ubuntu卸载python 后如何修复
  12. 用python实现自动签到脚本
  13. 运行c程序的步骤及方法
  14. 物联网卡与普通的sim卡的区别
  15. mybatisplus sql 改写2
  16. 带你入门NoSQL(真的是太全了)
  17. [嵌入式]汉字字模点阵
  18. xmind怎么导出为pdf?Xmind最全入门教程
  19. Docker | Docker 快速搭建 TensorRT 环境(超详细)
  20. GROMACS中mdp文件注解小结

热门文章

  1. 求视网膜oct图像数据集
  2. 程序员应该掌握的英语词汇
  3. 基于程序员职业对于婚配问题的研究——程序员为何单身?
  4. Facebook账户 “ 解封、防封、养号 ” 知识要点,已收藏!
  5. 梯形图转化c语言,PLC梯形图转换成STL程序
  6. 材料力学:使用matlab绘制铰支梁在多个集中力、集中力偶矩作用下的挠曲线
  7. 《Implicit Class-Conditioned Domain Alignment for Unsupervised Domain Adaptation》
  8. 《数字图像处理》-(3)-2频率域滤波
  9. Python 实现 2048 游戏
  10. 大学多宗持续灵异事件