使用JUnit规则进行干净的集成测试

JUnit Rules的优势,尤其是在进行集成测试时,几乎不能被高估。 在本文中,我们将阐明ExternalResource扩展的有用性。 在我们必须使用抽象外部资源的第三方库的情况下,这些简化了灯具控制。 作为示例,我们将看看如何基于Git提交日志消息来验证对条目列表的正确检索。

什么是集成测试?

“关注点分离”可能是软件设计和实现中最重要的单个概念。
语用单元测试[HUTH03]

通常,我们使用单元测试来检查一小段生产代码是否按预期工作。 但是重要的是要了解,这类测试仅限于开发人员负责的代码。 为了澄清这一点,请考虑合并第三方库来管理对文件,数据库,Web服务等的访问。

测试将隐式调用第三方组件的代码,因为我们的被测系统 (SUT)依赖于这些组件(DOC)[MESZ07]。 如果外部资源之一不可用,尽管开发人员的代码可能没有问题,但它们将失败。 此外,访问这些资源通常很慢,并且设置测试装置通常很麻烦。 更不用说脆弱性了,它是由不同库版本的潜在语义变化引起的。

所有这些缺点建议通过适配器抽象[FRPR10]将应用程序的代码与第三方代码分开。 不仅仅是抽象适配器组件可以在应用程序的问题域方面提供表达性的API,它还允许使用轻量级的替代测试double (通常表示为嘲笑)来替换基于第三方代码的实现。

使用JUnit进行测试

使用Junit书本测试 使用JUnit进行测试是Java开发人员可以学习的最有价值的技能之一。 无论您的背景是什么,无论您是只是想建立一个安全网以减少桌面应用程序的性能下降,还是要基于健壮且可重用的组件来提高服务器端的可靠性,都需要进行单元测试。

弗兰克(Frank)写了一本书,为使用JUnit进行测试的基本知识提供了深刻的切入点,并为您准备了与测试有关的日常工作挑战做好了准备。

学到更多…

这消除了先前列出的有关单元测试的依赖性问题。 双重测试的设置费用低廉,可以将测试中的系统与第三方代码隔离开,并保持测试的快速和可靠[MESZ07]。 但是,它剩下的工作是测试适配器组件的正确行为。 这是集成测试开始起作用的时候。

该术语指的是软件测试中的阶段,在该阶段中,将各个软件模块组合在一起并作为一个组进行测试[INTTES]。 公平地说,我们使用适配器抽象将一个或多个第三方模块组合在一起以提供某种功能。 因为从应用程序的角度来看,这样的适配器是低级组件,所以这种策略隐式地导致了一种自下而上的方法,即首先测试最低级别的组件,然后再使用它来促进对更高级别的组件的测试。

您可能想知道为测试目的调整设计是否不是一件坏事。 但是,通过使用适配器,您可以确定应用程序和第三方代码之间的明确界限。 如果新的库版本引入的行为略有不同,则只需调整适配器代码即可再次通过相应的集成测试。 您的实际应用程序代码(包括单元测试)将不受影响! 此外,您可以通过提供适当的适配器轻松切换到其他供应商。 因此,遵循这种做法还可以带来更健康的应用程序设计。 [APPE15]

外部资源处理

不幸的是,在编写集成测试时,我们必须面对通过使用测试倍数来避免单元测试所遇到的问题。 特别是从编码角度来看,安装测试夹具通常需要大量的精力。 最重要的是,我们还必须妥善保管[MESZ07]。 例如,这意味着我们可能需要在测试执行后重置外部资源的状态。 后者对于确保后续测试独立运行很重要。 这样一来,测试所做的资源修改就不会伪造其后继者的验证结果。

为了减少设置和拆卸代码的重复开销,将普通的段落交换到测试帮助程序类中似乎很自然。 考虑一下系统环境变量,主数据记录等的创建,删除或操作。 JUnit规则是特殊的测试助手,它像AOP框架一样拦截测试方法调用。 与AspectJ中的环境建议相比,它们可以在实际测试执行之前和/或之后做有用的事情。 例如,可以在测试运行之前注册REST服务资源,并在结束后自动将其删除。

JUnit为规则提供了方便的基类ExternalResource ,这些规则用于在测试之前设置外部资源(文件,套接字,服务器,数据库连接等),并保证随后将其拆除[EXRAPI]。 以下清单ServerRule显示了原理。

public class ServerRule extends ExternalResource {private final int port;public ServerRule( int port ) {this.port = port;}@Overrideprotected void before() throws Throwable {System.out.println( "start server on port: " + port );}@Overrideprotected void after() {System.out.println( "stop server on port: " + port );}
}

ServerRule的构造函数使用我们的虚拟服务器类型的端口号。 为了证明这个概念,实际上我们不启动一个真实的,但只打印出的调用含有消息这个号码beforeafter的回调挂钩。 下一个清单显示ServerRule的用法。

public class MyServerITest {@Rulepublic final ServerRule serverRule = new ServerRule( 5050 );@Testpublic void foo() {System.out.println( "code that fails without server access" ); }
}

请注意,该规则如何由带有@Rule注释的公共非静态字段@Rule 。 运行测试用例将导致以下输出。

start server on port: 5050
code that fails without server access
stop server on port: 5050

如您所见,该规则确保测试代码在预期的环境先决条件内执行,并自动进行内部管理。 为了加深这个主题,让我们看一个更详细的示例,该示例说明了规则管理的灯具与被测组件之间的相互作用。

设计用于Git集成测试的规则

标题图像显示了一个时间轴组件,该组件通过可配置的ItemProvider适配器检索其Item列表。 捕获图片时使用的适配器类型从Git存储库读取条目。 每个项目代表当前存储库分支的提交。 该插图基于我为《 Testing with JUnit》一书开发的示例应用程序的屏幕截图。 因为它超出了本书的范围,所以我借此机会对我申请编写JGit集成测试的GitRule助手进行了解释。

驱动程序是提供实用程序类,其目的是简化建立包含任意提交,分支等内容的git夹具存储库的任务。 为此,我创建了一个GitRepository类型。 这通过JGit处理本地存储库上的存储库交互。 以下摘录应阐明概念。

public class GitRepository {private final File location;GitRepository( File location ) {this.location = location;}public RevCommit commitFi1e( String fileName, String content, String message )throws IOException{createFi1e( fileName, content );addFi1es();return commit( message );}[...]
}

如您所见, GitRepository实例采用一个构造函数参数,该参数引用本地Git仓库的工作目录。 但是请注意构造函数的可见性限制。 这是因为抽象不负责处理存储库资源的生命周期。 对于后者,我们使用ExternalResource派生,如下面的清单所示。

public class GitRule extends ExternalResource {private final Set<File> repositories;public GitRule() {repositories = new HashSet<>();}@Overrideprotected void after() {repositories.forEach( repository -> delete( repository ) );}public GitRepository create( File location ) {createRepositoryOnDisk( location );GitRepository result = new GitRepository( location );repositories.add( location);return result;}private void createRepositoryOnDisk( File location ) {InitCommand init = Git.init();init.setDirectory( location );init.setBare( false );callInit( init );}private static void callInit( InitCommand init ) {try {init.call().close();} catch( GitAPIException exception ) {throw new GitOperationException( exception );}}
}

GitRule可以作为工厂存储特定测试所需的存储库资源。 此外,一旦完成测试执行,它就会跟踪正确处置所需的位置。 所示版本仅在磁盘上创建本地存储库,但是当然可以对其进行增强以克隆远程存储库。

ItemProvider接口依赖于扩展类型Item的通用类型参数。 因此, GitItemProvider类型返回GitItem实例作为查找结果,并且每个git项都是JGit RevCommit的封装。 这样说,很明显,第三方代码抽象可能会影响多个类。 以下代码段显示了一个简单的集成测试方案。 GitRule提供了适用于创建实际提交的存储库。 后者用于验证GitItem实例的正确实例化。

public class GitItemTest {@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();@Rule public final GitRule gitRule = new GitRule();@Testpublic void ofCommit() throws IOException {GitRepository repository = gitRule.create( temporaryFolder.newFolder() );RevCommit commit = repository.commitFi1e( "file", "content", "message"  );GitItem actual = GitItem.ofCommit( commit );assertThat( actual ).hasId( getId( commit ) ).hasTimeStamp( getTimeStamp( commit ) ).hasContent(  getContent( commit ) ).hasAuthor( getAuthor( commit ) );}[...]
}

该测试使用TemporaryFolder规则来确保在可访问目录下创建存储库。 实际上,使用临时文件夹规则应该使GitRule的资源删除GitRule多余。 但是,由于它的默认清除机制不会检查资源删除是否成功(无论如何,硬检查仅适用于最新的JUnit版本),所以我选择不依赖它。 这很重要,因为使用JGit可以很容易地遇到打开文件句柄的问题。

此外,测试的验证是通过定制的定制GitItemAssert断言类和一些实用程序方法(静态导入)完成的。 有了这个适当的位置之后,我们准备看一下更复杂的场景。

public class GitItemProviderITest {private static final String CLONE_NAME = "test";private static final int INITIAL_COMMIT_COUNT = 6;@Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder();@Rule public final GitRule gitRule = new GitRule();private GitRepository repository;private GitItemProvider provider;private File remoteLocation;private File destination;@Beforepublic void setUp() throws IOException {remoteLocation = temporaryFolder.newFolder();repository = createRepository( remoteLocation );destination = temporaryFolder.newFolder();provider = new GitItemProvider( remoteLocation.toURI().toString(),destination,CLONE_NAME );}@Testpublic void fetchItems() throws IOException {int fetchCount = INITIAL_COMMIT_COUNT / 3;List<GitItem> actual = provider.fetchItems( null, fetchCount );assertThat( actual ).isEqualTo( subList( 0, fetchCount ) ).hasSize( fetchCount );}private List<GitItem> subList( int fromIndex, int toIndex ) {return repository.logAll().stream().map( commit -> ofCommit( commit ) ).collect( toList() ).subList( fromIndex, toIndex );}[...]
}

设置与之前的测试相似。 但是,我们的装置存储库是通过委派给createRepository方法来创建的。 为了简洁起见,我在此省略了详细信息,因为该方法仅创建具有INITIAL_COMMIT_COUNT提交的存储库。 被测试的GitItemProvider组件采用三个构造函数参数。 第一个是夹具库的位置,它将由提供者克隆。 为此,第二个参数定义一个目标目录,而第三个参数将插入克隆存储库的文件夹名称。

在练习阶段,组件从其克隆的存储库中获取可用提交的子集。 subList验证该列表,该列表是由我们的灯具存储库中的方法subList计算得出的预期列表。 最后,这些规则负责客房整理。

如果要查看完整的示例代码,请参阅GitHub存储库https://github.com/fappel/Testing-with-JUnit上可用的示例应用程序源。

摘要

这篇文章介绍了如何在编写集成测试时将JUnit规则用于干净的资源管理。 我们已经对什么是集成测试有了基本的了解,了解了ExternalResource测试实用程序扩展的工作原理,并详细说明了使用示例。 当然,它的意义不仅仅在于初见。 熟悉此处显示的原理后,您可能会考虑研究其他主题,例如使用ClassRule来处理持久性固定装置, 规则链 , 环境变量等。

不能不告诉您我的书《 使用JUnit进行测试 》中的第6章“ 使用JUnit规则减少样板”可作为免费阅读样本, 网址为https://www.packtpub.com/packtlib/book/Application%20Development/ 9781782166603/6 。 如果您还不厌倦我的麻烦,请大胆前进并抓住机会深入研究JUnit规则的世界……

因此,请记住,人们始终遵守规则–不要忘记分享知识&#55357;&#56841;

资源资源

  • [APPE15]:Appel, 使用JUnit测试 ,Packt Publishing,2015年
  • [EXRAPI]:ExternalResource,API DOC, http ://junit.org/apidocs/org/junit/rules/ExternalResource.html
  • [FRPR10]:Freeman,Pryce, 不断发展的面向对象软件,由Tests指导 ,Addison Wesley,2010年
  • [HUTH03]:Hunt,Thomas, 实用单元测试有限公司,2003年,2004年
  • [INTTES]:Wikipedia,IntegrationTesting, https ://en.wikipedia.org/wiki/Integration_testing
  • [MESZ07]:Meszaros, xUnit测试模式 ,Pearson Education,Inc.,2007年

翻译自: https://www.javacodegeeks.com/2015/09/clean-integration-testing-with-junit-rules-3.html

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

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

相关文章

winform基础窗体设置及基础控件

WinForm - 也叫做C/S 客户端 另&#xff1a;B/S是 网页端 客户端应用程序 - 是需要安装在用户电脑上才可以使用的程序 特点&#xff1a; 不需要联网也可以打开使用部分功能&#xff0c;但是现在的情况是许多功能依然需要互联网的支持&#xff0c;代码部分在用户电脑上执行 使用…

【FastJSON】解决FastJson中“$ref 循环引用”的问题

0、开发环境 SSH&#xff0c;EasyUI&#xff0c;MySQL 1、需求要求&#xff1a; (1)首先获取所有的贷款订单数据&#xff0c;即List <LoanOrder>。 (2)然后从单个贷款订单实体LoanOrder去访问贷款人实体Loaner的信息。 2、实体之间的关系描述 (1)LoanOrder实体与Loaner…

JavaFX真实世界应用程序:EIZO CuratOR Caliop

JavaFX Real-World应用程序第四号称为Caliop 。 它是EIZO为医院手术室开发的CuratOR解决方案的前端。 前端在壁挂式控制台上运行&#xff0c;并允许操作团队查找有关患者的信息&#xff0c;控制各种视频源到不同监视器的路由&#xff0c;录制视频&#xff0c;拍摄照片/剧照。 …

netbeans代码提示_NetBeans可用性提示

netbeans代码提示的Java IDE都来了&#xff0c;因为在很长的路要走天的JBuilder的 &#xff08;尽管JBuilder中似乎是一个值得欢迎提前在时间&#xff09;。 当今的Java IDE&#xff08;例如NetBeans &#xff0c; Eclipse &#xff0c; IntelliJ IDEA和JDeveloper &#xff09…

推荐文章:机器学习:“一文读懂机器学习,大数据/自然语言处理/算法全有了...

PS:文章主要转载自CSDN大神"黑夜路人"的文章: http://blog.csdn.NET/heiyeshuwu/article/details/43483655 本文主要对机器学习进行科普,包括机器学习的定义、范围、方法,包括机器学习的研究领域&#xff1a;模式识别、计算机视觉、语音识别、自然语言…

java比较炫的登录界面_html+css实现漂亮的透明登录页面,HTML实现炫酷登录页面...

承蒙各位小伙伴的支持&#xff0c;鄙人有幸入围了《CSDN 2020博客之星》的前200名&#xff0c;现在进入投票环节&#xff0c;如果我平时写的文章和分享对你有用的话&#xff0c;请每天点击一下这个链接&#xff0c;投上你们宝贵的一票吧&#xff01;谢谢&#xff01;❤️ 每一票…

OpenMap教程第2部分–使用MapHandler构建基本地图应用程序–第1部分

1.简介 在第一个教程中&#xff0c;我们创建了一个基本的OpenMap GIS应用程序&#xff0c;该应用程序在JFrame中显示一个从文件系统加载的具有一个形状图层的地图。 该教程基于com.bbn.openmap.app.example.SimpleMap 。 在该教程中&#xff0c;我们使用了以下OpenMap类&#x…

java rx.observable_Rxjava2 Observable的条件操作符详解及实例

简要&#xff1a;需求了解&#xff1a;在使用 Rxjava 开发中&#xff0c;经常有一些各种条件的操作 &#xff0c;如比较两个 Observable 谁先发射了数据、跳过指定条件的 Observable 等一系列的条件操作需求&#xff0c;那么很幸运&#xff0c; Rxjava 中已经有了很多条件操作符…

Linux poll 和 select 机制

poll select 介绍 使用非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个 或多个文件而不阻塞. 这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来 读或写.…

hprof 不大 泄露_HPROF –内存泄漏分析教程

hprof 不大 泄露本文将为您提供有关如何通过生成和分析Sun HotSpot JVM HPROF堆转储文件来分析JVM内存泄漏问题的教程。 一个现实的案例研究将用于此目的&#xff1a;Weblogic 9.2内存泄漏影响Weblogic Admin服务器。 环境规格 Java EE服务器&#xff1a;Oracle Weblogic Ser…

投资银行对Java进行的二十大核心面试问答

这是在金融领域&#xff08;主要是大型投资银行&#xff09;共享Java核心访谈问题和答案的新系列。 在JP Morgan&#xff0c;Morgan Stanley&#xff0c;Barclays或Goldman Sachs上会问许多这些Java面试问题。 银行主要从多线程 &#xff0c; 集合 &#xff0c;序列化&#xff…

java 支付重复问题_Airbnb支付系统如何在分布式环境下避免重复打款

原文链接&#xff1a;https://medium.com/airbnb-engineering/avoiding-double-payments-in-a-distributed-payments-system-2981f6b070bbAirbnb一直在将其基础架构迁移到面向服务的体系结构(SOA)。 SOA具有许多优势&#xff0c;例如使开发人员能够专业化并具有更快迭代的能力。…

myeclipse配置java8_MyEclipse 2017配置Tomcat8

MyEclipse 2017的配置其实跟MyEclipse 2015的比较一致&#xff0c;再往之前的就有出入了。不得不吐槽其卡&#xff0c;跟Eclipse不在一个量级上呀。。。一、配置转自&#xff1a;http://www.xuexila.com/diannao/diy/2788109.html工具/原料myeclipse 2017tomcat8 服务器方法/步…

MySQL在Django框架下的基本操作(MySQL在Linux下配置)

注&#xff1a;本文已迁移至CSDN&#xff0c;后续的更新也会在CSDN。 http://blog.csdn.net/houchaoqun_xmu/article/details/53813633 http://blog.csdn.net/houchaoqun_xmu 【原】本文根据实际操作主要介绍了Django框架下MySQL的一些常用操作&#xff0c;核心内容如下&#x…

Clean Sheet – Windows 10的人体工程学Eclipse主题

Clean Sheet是适用于Windows 10的符合人体工程学的Eclipse主题。它基于干净&#xff0c;低眩光的外观和感觉&#xff0c;旨在减轻视觉疲劳和眼睛疲劳。 它融合了均衡的颜色选择&#xff0c;可以突出谐波语法并注重可读性。 除自定义滚动条外&#xff0c;它还努力满足现代美学要…

CentOS上安装MyCat-MySQL

1、安装JDK&#xff0c;要求JDK7以上。 2、下载MyCat&#xff0c;地址。 3、解压Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz&#xff0c;到usr/local/Mycat目录下。 4、添加用户Mycat。 [rootlocalhost Desktop]# useradd Mycat [rootlocalhost Desktop]# passwd M…

Java中的Redis的思维导图_Redis思维导图

常见相关问题Redis 有哪些功能&#xff1f;数据缓存功能分布式锁的功能支持数据持久化支持事务支持消息队列Redis 为什么是单线程的&#xff1f;因为 cpu 不是 Redis 的瓶颈&#xff0c;Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现&#xff0c;而且 cpu 又…

在Java EE应用程序中实现自动重试

最初&#xff0c;我想将此博客称为“ 具有拦截器驱动的重试策略的灵活超时 ”&#xff0c;但后来我认为它太“繁重”。 该声明以及修改后的标题应该&#xff08;希望&#xff09;使您了解此帖子可能谈论的内容;-) 触发 这篇文章主要由我在较早的一篇文章中收到的评论/问题之一…

2016-12-17 新浪博客服务器挂掉了,所有博客页面都无法打开

今天&#xff08;2016-12-17 10:20&#xff09;早晨从百度上检索到一篇新浪博客文章&#xff0c;点进去之后显示无法访问此网址&#xff0c;如下截图 去新浪博客主页看了看&#xff0c;主页是可以打开的 但是点进去任何一篇文章&#xff0c;都无法打开此页面 应该是新浪博客的服…

php版本7历史,php的版本发展历史(1995-2020)

PHP一直作为Web开发中的统治力量而存在&#xff0c;在WEB服务端开发领域&#xff0c;全球份额始终保持在78%以上。PHP快速&#xff0c;非常强大&#xff0c;生态好&#xff0c;而且免费&#xff0c;是一个为WEB而生的编程语言&#xff0c;自从诞生起PHP就被大多数开发者称为世界…