简而言之:JRunner

关于JUnit测试要点的多篇教程的第四章介绍了该工具可交换测试运行器体系结构的目的,并介绍了一些可用的实现。 正在进行的示例通过编写参数化测试的不同可能性扩大了主题。

由于我已经发布了JUnit Rules的介绍,因此我决定跳过关于该主题的已宣布部分。 相反,我对后者进行了较小的更新。

测试跑步者架构

不要害怕为了伟大而放弃美好。
约翰·洛克菲勒

在先前的文章中,我们学习了将某些xUnit测试模式[MES]与JUnit一起使用。 工具运行时的默认行为很好地支持了这些概念。 但是有时需要针对特定​​的测试类型或目标更改或补充后者。

考虑例如集成测试 ,该测试通常需要在特定环境中运行。 或想象一组包含子系统规范的测试用例,应该为常见的测试执行而组成。

为此,JUnit支持使用各种类型的测试处理器。 因此,它在运行时将测试类实例化,测试执行和结果报告委托给此类处理器,这些处理器必须是org.junit.Runner子类型。

测试用例可以使用@RunWith注释指定其预期的运行器类型。 如果未指定任何类型,那么运行时将选择BlockJUnit4ClassRunner作为默认值。 负责每个测试运行一个新的测试实例,并调用诸如隐式设置或拆卸处理程序之类的生命周期方法(另请参见有关测试结构的章节)。

@RunWith( FooRunner.class )
public class BarTest {

该代码段显示了如何将虚构的FooRunner指定为也是虚构的BarTest测试处理器。

通常,无需编写自定义测试运行程序。 但是,如果需要的话, Michael Scharhag最近就对JUnit的运行器体系结构进行了很好的解释。

看起来特殊测试运行程序的用法很简单,所以让我们看一下其中的几个:

套房和类别

Suite可能是最著名的处理器之一。 它允许以分层或主题结构的方式运行测试和/或其他套件的集合。 注意,指定类本身通常没有主体实现。 它带有一系列测试类的注释,这些类通过运行套件来执行:

@RunWith(Suite.class)
@SuiteClasses( { NumberRangeCounterTest.class,// list of test cases and other suites
} )
public class AllUnitTests {}

但是,套件的结构功能受到一定限制。 因此,JUnit 4.8引入了鲜为人知的Categories概念。 这样就可以定义自定义类别类型,例如单元测试,集成测试和验收测试。 要将测试用例或方法分配给这些类别之一,必须提供Category注释:

// definition of the available categories
public interface Unit {}
public interface Integration {}
public interface Acceptance {}// category assignment of a test case
@Category(Unit.class)
public class NumberRangeCounterTest {[...]
}// suite definition that runs tests
// of the category 'Unit' only
@RunWith(Categories.class)
@IncludeCategory(Unit.class)
@SuiteClasses( { NumberRangeCounterTest.class,// list of test cases and other suites
} )
public class AllUnitTests {}

Categories注释类定义了只运行与指定类别匹配的类列表测试的套件。 通过包含和/或排除注释来完成规范。 请注意,类别可以在Maven或Gradle构建中使用,而无需定义特定的套件类(请参见JUnit文档的类别部分)。

有关类别的更多信息: John Ferguson Smart撰写了有关使用JUnit类别对测试进行分组的详细说明。

由于通常认为套件类列表和类别注释的维护有些繁琐,因此您可能更喜欢通过测试后缀名称àFooUnitTest而不是FooTest进行分类。 这允许在运行时在类型范围上过滤类别。

但是JUnit本身不支持此过滤,因此您可能需要一个特殊的运行器来动态收集可用的匹配测试。 Johannes Link的ClasspathSuite是提供适当实现的库。 如果您碰巧在OSGi环境中进行集成测试,则Rüdiger的BundleTestSuite会对捆绑BundleTestSuite执行类似的操作。

在对如何将测试运行程序用于测试捆绑的第一印象之后,让我们继续本教程的示例,并进行一些更令人兴奋的事情。

参数化测试

本教程中使用的示例都是关于编写一个简单的数字范围计数器,该计数器从给定值开始传递一定数量的连续整数。 另外,计数器取决于存储类型以保留其当前状态。 有关更多信息,请参阅前面的章节。

现在,假定应将由构造函数参数初始化的NumberRangeCounter作为API提供。 因此,我们可以认为实例创建检查给定参数的有效性是合理的。

我们可以指定适当的极端情况,并通过每个测试通过IllegalArgumentException予以确认。 使用带有Java 8 Lambdas方法的Clean JUnit Throwable-Tests方法,这种验证storage参数一定不能为null的测试可能看起来像这样:

@Testpublic void testConstructorWithNullAsStorage() {Throwable actual = thrown( () -> new NumberRangeCounter( null, 0, 0 ) );assertTrue( actual instanceof IllegalArgumentException );assertEquals( NumberRangeCounter.ERR_PARAM_STORAGE_MISSING,actual.getMessage() );}

请注意,我坚持使用JUnit内置功能进行验证。 我将在另一篇文章中介绍特定匹配器库( Hamcrest , AssertJ )的优缺点。

为了使该职位保持范围,我还跳过了有关NPE是否比IAE更好的讨论。

如果我们必须涵盖很多这种极端情况,则上述方法可能会导致很多非常相似的测试。 JUnit提供了Parameterized实现,以减少这种冗余。 这个想法是为通用测试结构提供各种数据记录。

为此,使用带有@Parameters注释的公共静态方法来创建数据记录作为对象数组的集合。 此外,测试用例需要一个带有参数的公共构造函数,该参数与记录提供的数据类型匹配。

参数化处理器针对由参数方法提供的每个记录运行给定测试。 这意味着对于每种测试和记录组合,都会创建一个新的测试类实例。 构造函数参数将存储为字段,并且可以通过测试进行访问以进行设置,练习和验证:

@RunWith( Parameterized.class )
public class NumberRangeCounterTest {private final String message;private final CounterStorage storage;private final int lowerBound;private final int range;@Parameterspublic static Collection<Object[]> data() {CounterStorage dummy = mock( CounterStorage.class );return Arrays.asList( new Object[][] { { NumberRangeCounter.ERR_PARAM_STORAGE_MISSING, null, 0, 0 }, { NumberRangeCounter.ERR_LOWER_BOUND_NEGATIVE, dummy, -1, 0 },[...] // further data goes here... } );}public NumberRangeCounterTest(String message, CounterStorage storage, int lowerBound, int range ){this.message = message;this.storage = storage;this.lowerBound = lowerBound;this.range = range;}@Testpublic void testConstructorParamValidation() {Throwable actual = thrown( () -> new NumberRangeCounter( storage, lowerBound, range ) );assertTrue( actual instanceof IllegalArgumentException );assertEquals( message, actual.getMessage() );}[...]
}

尽管该示例确实减少了测试冗余,但至少在可读性方面值得商bat。 最后,这通常取决于测试的数量和特定测试数据的结构。 但绝对不幸的是, 使用任何记录值的测试也将执行多次。

因此,参数化测试通常保存在单独的测试用例中,通常感觉更像是一种解决方法,而不是适当的解决方案。 因此,一个聪明的人想到了提供一种可以避免上述问题的测试处理器的想法。

JUnitParams

JUnitParams库提供JUnitParamsRunner@Parameter类型。 param批注指定给定测试的数据记录。 注意使用相同简单名称的JUnit批注的区别。 后者标记了提供数据记录的方法!

可以使用JUnitParams重写上面的测试方案,如以下代码片段所示:

@RunWith( JUnitParamsRunner.class )
public class NumberRangeCounterTest {public static Object data() {CounterStorage dummy = mock( CounterStorage.class );return $( $( ERR_PARAM_STORAGE_MISSING, null, 0, 0 ),$( ERR_LOWER_BOUND_NEGATIVE, dummy, -1, 0 ) );  }@Test@Parameters( method = "data" )public void testConstructorParamValidation(String message, CounterStorage storage, int lowerBound, int range ) {Throwable actual = thrown( () -> new NumberRangeCounter( storage, lowerBound, range ) );assertTrue( actual instanceof IllegalArgumentException );assertEquals( message, actual.getMessage() );}[...]
}

虽然这当然更紧凑并且乍一看看上去更干净,但仍有一些构造需要进一步说明。 $(...)方法是在JUnitParamsRunner (静态导入)中定义的,是创建对象数组的快捷方式。 一旦习惯了,数据定义就会变得更具可读性。

$快捷方式用于方法data以创建嵌套的对象数组作为返回值。 尽管运行程序期望在运行时嵌套数据数组,但它能够将简单的对象类型作为返回值来处理。

测试本身具有一个附加的@Parameters批注。 批注的方法声明是指用于为测试提供声明的参数的数据提供程序 。 方法名称在运行时通过反射解析。 这是解决方案的缺点,因为它在编译时不安全。

但是在其他用例场景中,您可以指定数据提供程序类或隐式值,因此不会受到这种折衷的影响。 有关更多信息,请参见例如该库的快速入门指南 。

另一个巨大的优点是,现在只有那些测试针对使用@Parameters批注的数据记录运行。 标准测试仅执行一次。 反过来,这意味着可以将参数化测试保留在设备的默认测试用例中。

junit-params-test

包起来

以上各节概述了JUnit可交换测试运行器体系结构的意义和目的。 它介绍了套件和类别以显示基本用法,并举例说明了测试运行程序如何简化编写与数据记录相关的测试的任务。

有关其他测试运行程序的列表,junit.org上的“ 测试运行程序”和“ 自定义运行程序 ”页面可能是一个不错的起点。 并且,如果您想知道标题图片的Theories主题是什么,可以看看Florian Waibels在JUnit上 发表的文章– Practice和@Theory之间的区别 。

下次在Nutshell中使用JUnit时,我最后将介绍可用于验证测试结果的各种断言。

参考文献

[MES] xUnit测试模式,Gerard Meszaros,2007年

翻译自: https://www.javacodegeeks.com/2014/09/junit-in-a-nutshell-test-runners.html

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

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

相关文章

cmake how to create vs file filters

cmake how to create vs file filters 用cmakelists构建出来的工程&#xff0c;没有文件filters&#xff0c;可采用如下方法解决 set(SOURCE_LIST"lotteryTicket.cpp""stdafx.cpp""stdafx.h""test/main.cpp" )add_executable(lotteryT…

Hibernate核心接口

一、Configuration类&#xff1a;1、 作用&#xff1a;&#xff08;1&#xff09;管理hibernate配置信息&#xff08;2&#xff09;读取hibernate.cfg.xml文件&#xff08;3&#xff09;加载hibernate的驱动&#xff0c;例如&#xff1a;url,用户名&#xff08;4&#xff09;管…

CSS实现垂直居中的方法

CSS实现垂直居中的方法 1、relative absolute定位&#xff1a; (1)css html代码 1 <!doctype html>2 <html lang"en">3 4 <head>5 <meta charset"UTF-8" />6 <title>Document</title>7 …

高并发系统之大忌-慢查询

最近又遇到了一次慢查把db&#xff08;mariadb10)几乎打挂的案例&#xff0c;作为一个核心支付系统的技术负责人&#xff0c;真是每日如履薄冰。因为之前支付系统经常出问题&#xff0c;现在各个BG对支付系统都盯得很紧。这次要不是我及时让DB给暴力清理数据&#xff0c;没准又…

Hadoop namenode启动瓶颈分析

转载&#xff1a;http://blog.csdn.net/AE86_FC/archive/2010/08/26/5842020.aspx NameNode启动过程详细剖析 NameNode中几个关键的数据结构 FSImage Namenode会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中&#xff0c;每次保存fsimage之后到下次保存之间的所有…

Java 9 –终极功能列表

这篇文章将针对即将到来的Java 9版本进行更新&#xff0c;新增功能 &#xff08; 最新更新&#xff1a;2014年 9月9日 &#xff09; OpenJDK开发正在加快速度&#xff1a;2014年3月Java 8发布之后&#xff0c;我们预计将进入2年的发布周期。 据报道&#xff0c;Java 9将于2016…

js中的作用域和作用域链

作用域就是变量与函数的可访问范围。在js中只有 全局作用域 和 函数作用域 &#xff0c;并没有块级作用域。 全局作用域 在所有函数外定义的变量、声明的函数就是全局作用域&#xff0c;在全部环境下都可以访问。 var a 111;function fn(){console.log(a); }fn(); // 打印了…

vue打包后不使用服务器直接访问方法

根据官网打包执行npm run build 后dist文件夹打开的index.html 是空白 需要开启http服务器才能访问&#xff0c;以下是解决办法 1、找到config文件夹下的index文件 修改成 2、找到build文件夹下的until文件 修改成 然后执行npm run build重新打包下就ok了 更多专业前端知…

OpenStack虚机网卡的创建过程

原文&#xff1a;https://www.sdnlab.com/20286.htmlOpenStack最基本和常用的操作就是启动虚机。虚机启动的过程中涉及很多内容&#xff0c;其中非常重要的一个环节就是创建并绑定虚机的虚拟网卡。虚机的创建和管理是Nova的任务&#xff0c;虚机网络的创建和管理是Neutron的任务…

js中的原型与原型链

js的学习有三座大山&#xff0c; 原型/原型链 、 作用域/闭包 、 异步/单线程&#xff0c;这三个知识点虽然基础但是入门时理解起来比较困难&#xff0c;本文先整理总结原型和原型链这一知识点。 1. 原型链怎么来的&#xff1f;对象的原型和function的prototype属性有什么关系…

HTML5 audio 如何实现播放多个MP3音频

<audio>标签是HTML5中的新标签&#xff0c;定义声音用于嵌入音频内容&#xff0c;比如音乐或其他音频流。用的比较多音频格式是.mp3。 <audio>标签常用属性如下表 属性值描述autoplayautoplay添加该属性后&#xff0c;音频会自动播放controlscontrols设置后&…

windwos下ntp服务器配置 arm平台ntp客户端获取同步时间

项目需要使用同步时间&#xff0c;在arm-linux开发板上&#xff0c;移植了ntp客户端&#xff0c;查看了一些资料&#xff0c;最终发现使用windows自带的ntp服务器比较方便&#xff0c;而且很靠谱&#xff0c;使用配置了一番&#xff0c;已经能够正常使用 详细步骤&#xff1a; …

BOM(Browser Object Model)

BOM&#xff08;浏览器对象模型&#xff09;&#xff0c;提供了一系列操作浏览器&#xff0c;获取浏览器信息的接口。这些接口在平时的工作中会经常用到&#xff0c;例如当前页面的刷新&#xff0c;获取url的参数等等。 注&#xff1a;图片来自 http://www.dreamdu.com/javascr…

入门 IT 行业,该具备哪些技能?

对于刚开始进入IT的新人来说&#xff0c;“必备技能”往往意味着一个长长的、标有重要度的学习列表&#xff0c;但是过长的列表通常会导致新人不知如何开始学习&#xff0c;压力倍增。本文尝试列举出最重要的几个技能&#xff0c;也期望通过此列表能给新人一个比较明确的学习重…

实验七作业

Part 1:验证性实验 将line29&#xff1a;for(i0;i<N;i)改为while(!feof(fp)) // 从文本文件file1.dat中读取数据&#xff0c;找出最高分和最低分学生信息&#xff0c;并输出在屏幕上 #include <stdio.h> #include <stdlib.h>#define N 10// 定义一个结构体类型…

块级格式化上下文(Block Formatting Context)

CSS块级格式化上下文是块级盒子的一种能力&#xff0c;这种能力并不是直接通过css属性声明而获得的&#xff0c;而是添加css的一部分相关属性之后自动获得的能力&#xff0c;也就是说没有一个明确的属性就是生成块级格式化上下文的。 块级格式化上下文的能力就是让具有该能力的…

前端性能优化方法总结

一个网站前端性能的好坏很大程度上影响了用户愿不愿意使用访问这个网站&#xff0c;因此对前端进行性能优化是个很重要的事情。  对于前端性能优化这个问题&#xff0c;主要学习自yahoo前端性能团队总结的35条黄金定律总结&#xff0c;觉得很全很赞&#xff0c;做个学习总结和…

Akka笔记–演员介绍

过去做过多线程的任何人都不会否认管理多线程应用程序有多么艰辛和痛苦。 我说管理是因为它一开始很简单&#xff0c;一旦您开始看到性能改进&#xff0c;它就会变得非常有趣。 但是&#xff0c;当您发现没有一种简单的方法可以从子任务中的错误或难以发现的僵尸错误中恢复时&a…

Java英雄:丹·艾伦

“ Java英雄 ”系列休息了很长时间。 老实说&#xff0c;我想即使有很多人想在这里收录&#xff0c;它也可能会以虚无收场。 其中之一是丹。 我第一次要求他捐款已经将近一年半了&#xff0c;与此同时发生的一切&#xff0c;让我不再有任何答案就让我安心了。 但是以下内容在Ja…

Java-Class-I:java.util.List

ylbtech-Java-Class-I&#xff1a;java.util.List1.返回顶部 1.1、import java.util.ArrayList;import java.util.List; 1.2、List<Integer> newList new ArrayList<Integer>();newList.add(3); 2、 2.返回顶部1.1、import java.util.*;public class Test{public …