通过beforeClass和afterClass设置增强Spring Test Framework

如何允许实例方法作为JUnit BeforeClass行为运行

JUnit允许您在所有测试方法调用之前和之后一次在类级别上设置方法。 但是,通过有意设计,他们将其限制为仅使用@BeforeClass@AfterClass批注的静态方法。 例如,此简单的演示显示了典型的Junit设置:

package deng.junitdemo;import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;public class DemoTest {@Testpublic void testOne() {System.out.println('Normal test method #1.');}@Testpublic void testTwo() {System.out.println('Normal test method #2.');}@BeforeClasspublic static void beforeClassSetup() {System.out.println('A static method setup before class.');}@AfterClasspublic static void afterClassSetup() {System.out.println('A static method setup after class.');}
}

并应产生以下输出:

A static method setup before class.
Normal test method #1.
Normal test method #2.
A static method setup after class.

在大多数情况下,此用法都可以,但是有时候您想使用非静态方法来设置测试。 稍后,我将向您展示更详细的用例,但是现在,让我们看看如何首先使用JUnit解决这个顽皮的问题。 我们可以通过使测试实现一个提供before和after回调的Listener来解决此问题,并且需要挖掘JUnit来检测此Listener来调用我们的方法。 这是我想出的解决方案:

package deng.junitdemo;import org.junit.Test;
import org.junit.runner.RunWith;@RunWith(InstanceTestClassRunner.class)
public class Demo2Test implements InstanceTestClassListener {@Testpublic void testOne() {System.out.println('Normal test method #1');}@Testpublic void testTwo() {System.out.println('Normal test method #2');}@Overridepublic void beforeClassSetup() {System.out.println('An instance method setup before class.');}@Overridepublic void afterClassSetup() {System.out.println('An instance method setup after class.');}
}

如上所述,我们的监听器是一个简单的合同:

package deng.junitdemo;public interface InstanceTestClassListener {void beforeClassSetup();void afterClassSetup();
}

我们的下一个任务是提供将触发设置方法的JUnit运行器实现。

package deng.junitdemo;import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;public class InstanceTestClassRunner extends BlockJUnit4ClassRunner {private InstanceTestClassListener InstanceSetupListener;public InstanceTestClassRunner(Class<?> klass) throws InitializationError {super(klass);}@Overrideprotected Object createTest() throws Exception {Object test = super.createTest();// Note that JUnit4 will call this createTest() multiple times for each// test method, so we need to ensure to call 'beforeClassSetup' only once.if (test instanceof InstanceTestClassListener && InstanceSetupListener == null) {InstanceSetupListener = (InstanceTestClassListener) test;InstanceSetupListener.beforeClassSetup();}return test;}@Overridepublic void run(RunNotifier notifier) {super.run(notifier);if (InstanceSetupListener != null)InstanceSetupListener.afterClassSetup();}
}

现在我们从事业务。 如果我们在测试之上运行,它应该会给我们类似的结果,但是这次我们使用的是实例方法!

An instance method setup before class.
Normal test method #1
Normal test method #2
An instance method setup after class.


一个具体的用例:使用Spring Test Framework

现在,让我向您展示一个上面的真实用例。 如果使用Spring Test Framework,通常会设置这样的测试,以便可以将测试治具作为成员实例注入。

package deng.junitdemo.spring;import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SpringDemoTest {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));}
}

您还需要在同一包下的spring xml才能运行:

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd'><bean id='myList' class='java.util.ArrayList'><constructor-arg><list><value>one</value><value>two</value></list></constructor-arg></bean>
</beans>

非常注意成员实例List<String> myList 。 运行JUnit测试时,Spring将注入该字段,并且可以在任何测试方法中使用它。 但是,如果您想一次性设置一些代码并获得对Spring注入字段的引用,那么您很不幸。 这是因为JUnit @BeforeClass将强制您的方法为静态方法。 如果您将字段设为静态,则在测试中无法使用Spring注入!

现在,如果您是经常使用Spring的用户,您应该知道Spring Test Framework已经为您提供了一种处理此类用例的方法。 这是一种使用Spring样式进行类级别设置的方法:

package deng.junitdemo.spring;import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AbstractTestExecutionListener;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners(listeners = {DependencyInjectionTestExecutionListener.class, SpringDemo2Test.class})
@ContextConfiguration
public class SpringDemo2Test extends AbstractTestExecutionListener {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));}@Overridepublic void afterTestClass(TestContext testContext) {List<?> list = testContext.getApplicationContext().getBean('myList', List.class);assertThat((String)list.get(0), is('one'));}@Overridepublic void beforeTestClass(TestContext testContext) {List<?> list = testContext.getApplicationContext().getBean('myList', List.class);assertThat((String)list.get(1), is('two'));}
}

如您所见,Spring提供了@TestExecutionListeners批注,以允许您编写任何侦听器,并且其中将包含对TestContext的引用,该引用具有ApplicationContext以便您获取注入的字段引用。 这行得通,但我觉得它不是很优雅。 当您注入的字段已经可以用作字段时,它会强制您查找bean。 但是除非您通过TestContext参数,否则您将无法使用它。

现在,如果您混合了开始时提供的解决方案,我们将看到更漂亮的测试设置。 让我们来看看它:

package deng.junitdemo.spring;import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;import java.util.List;import javax.annotation.Resource;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;import deng.junitdemo.InstanceTestClassListener;@RunWith(SpringInstanceTestClassRunner.class)
@ContextConfiguration
public class SpringDemo3Test implements InstanceTestClassListener {@Resource(name='myList')private List<String> myList;@Testpublic void testMyListInjection() {assertThat(myList.size(), is(2));}@Overridepublic void beforeClassSetup() {assertThat((String)myList.get(0), is('one'));}@Overridepublic void afterClassSetup() {assertThat((String)myList.get(1), is('two'));}
}

现在,JUnit仅允许您使用单个Runner ,因此我们必须扩展Spring的版本以插入之前的操作。

package deng.junitdemo.spring;import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.InitializationError;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import deng.junitdemo.InstanceTestClassListener;public class SpringInstanceTestClassRunner extends SpringJUnit4ClassRunner {private InstanceTestClassListener InstanceSetupListener;public SpringInstanceTestClassRunner(Class<?> clazz) throws InitializationError {super(clazz);}@Overrideprotected Object createTest() throws Exception {Object test = super.createTest();// Note that JUnit4 will call this createTest() multiple times for each// test method, so we need to ensure to call 'beforeClassSetup' only once.if (test instanceof InstanceTestClassListener && InstanceSetupListener == null) {InstanceSetupListener = (InstanceTestClassListener) test;InstanceSetupListener.beforeClassSetup();}return test;}@Overridepublic void run(RunNotifier notifier) {super.run(notifier);if (InstanceSetupListener != null)InstanceSetupListener.afterClassSetup();}
}

这应该够了吧。 运行测试将使用以下输出:

12:58:48 main INFO  org.springframework.test.context.support.AbstractContextLoader:139 | Detected default resource location 'classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml' for test class [deng.junitdemo.spring.SpringDemo3Test].
12:58:48 main INFO  org.springframework.test.context.support.DelegatingSmartContextLoader:148 | GenericXmlContextLoader detected default locations for context configuration [ContextConfigurationAttributes@74b23210 declaringClass = 'deng.junitdemo.spring.SpringDemo3Test', locations = '{classpath:/deng/junitdemo/spring/SpringDemo3Test-context.xml}', classes = '{}', inheritLocations = true, contextLoaderClass = 'org.springframework.test.context.ContextLoader'].
12:58:48 main INFO  org.springframework.test.context.support.AnnotationConfigContextLoader:150 | Could not detect default configuration classes for test class [deng.junitdemo.spring.SpringDemo3Test]: SpringDemo3Test does not declare any static, non-private, non-final, inner classes annotated with @Configuration.
12:58:48 main INFO  org.springframework.test.context.TestContextManager:185 | @TestExecutionListeners is not present for class [class deng.junitdemo.spring.SpringDemo3Test]: using defaults.
12:58:48 main INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 | Loading XML bean definitions from class path resource [deng/junitdemo/spring/SpringDemo3Test-context.xml]
12:58:48 main INFO  org.springframework.context.support.GenericApplicationContext:500 | Refreshing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 29 12:58:48 EDT 2012]; root of context hierarchy
12:58:49 main INFO  org.springframework.beans.factory.support.DefaultListableBeanFactory:581
| Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@73c6641: defining beans [myList,org.springframework.context.annotation.
internalConfigurationAnnotationProcessor,org.
springframework.context.annotation.internalAutowiredAnnotationProcessor,org
.springframework.context.annotation.internalRequiredAnnotationProcessor,org.
springframework.context.annotation.internalCommonAnnotationProcessor,org.
springframework.context.annotation.
ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy
12:58:49 Thread-1 INFO  org.springframework.context.support.GenericApplicationContext:1025 | Closing org.springframework.context.support.GenericApplicationContext@44c9d92c: startup date [Sat Sep 29 12:58:48 EDT 2012]; root of context hierarchy
12:58:49 Thread-1 INFO  org.springframework.beans.factory.support.
DefaultListableBeanFactory:433 
| Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@
73c6641: defining beans [myList,org.springframework.context.annotation.
internalConfigurationAnnotationProcessor,org.springframework.
context.annotation.internalAutowiredAnnotationProcessor,org.springframework.
context.annotation.internalRequiredAnnotationProcessor,org.springframework.
context.annotation.internalCommonAnnotationProcessor,org.springframework.
context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0]; root of factory hierarchy

显然,输出在这里没有显示任何有趣的内容,但是测试应该在所有声明通过的情况下运行。 关键是,现在我们有一种更优雅的方法来调用类级别的测试之前和之后的测试,并且它们可以是允许Spring注入的实例方法。

下载演示代码

您可能会从我的沙箱中获得一个正常运行的Maven项目中的演示代码

参考: A程序员杂志博客上的JCG合作伙伴 Zemian Deng提供的beforeClass和afterClass设置增强了Spring Test Framework 。


翻译自: https://www.javacodegeeks.com/2012/10/enhancing-spring-test-framework-with.html

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

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

相关文章

华为鸿蒙出来正当时,关于华为鸿蒙操作系统,中兴率先表态

原标题&#xff1a;关于华为鸿蒙操作系统&#xff0c;中兴率先表态 来源&#xff1a;科技数码迷进入2021年之后中兴这个品牌的存在感越来越强了&#xff0c;并且还学会了借势营销。每当国内智能手机领域有大事之时总会看到中兴或红魔手机的身影。这说明在5G过渡期中兴要借个机会…

条件变量(Condition Variable)详解

转载于&#xff1a;http://blog.csdn.net/erickhuang1989/article/details/8754357 条件变量(Condtion Variable)是在多线程程序中用来实现“等待->唤醒”逻辑常用的方法。举个简单的例子&#xff0c;应用程序A中包含两个线程t1和t2。t1需要在bool变量test_cond为true时才能…

C++中的深拷贝和浅拷贝 QT中的深拷贝,浅拷贝和隐式共享

下面是C中定义的深&#xff0c;浅拷贝 当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候&#xff0c;拷贝构造函数就会被自动调用。也就是说&#xff0c;当类的对象需要拷贝时&#xff0c;拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数&#…

使用PowerMock模拟构造函数

我认为&#xff0c;依赖项注入的主要好处之一是可以将模拟和/或存根对象注入代码中&#xff0c;以提高可测试性&#xff0c;增加测试覆盖率并编写更好&#xff0c;更有意义的测试。 但是&#xff0c;有时候您会遇到一些不使用依赖注入的传统代码&#xff0c;而是通过组合而不是…

Brackets (区间DP)

个人心得&#xff1a;今天就做了这些区间DP&#xff0c;这一题开始想用最长子序列那些套路的&#xff0c;后面发现不满足无后效性的问题&#xff0c;即&#xff08;&#xff0c;&#xff09;的配对 对结果有一定的影响&#xff0c;后面想着就用上一题的思想就慢慢的从小一步一步…

android生成aar无效,android studio生成aar包并在其他工程引用aar包的方法

1.aar包是android studio下打包android工程中src、res、lib后生成的aar文件&#xff0c;aar包导入其他android studio 工程后&#xff0c;其他工程可以方便引用源码和资源文件2.生成aar包步骤&#xff1a;①.用android studio打开一个工程&#xff0c;然后新建一个Module&#…

《剑指offer》— JavaScript(3)从尾到头打印链表

从尾到头打印链表 题目描述 输入一个链表&#xff0c;从尾到头打印链表每个节点的值。 实现代码 /*function ListNode(x){this.val x;this.next null; }*/ function printListFromTailToHead(head) {var res[];while(head){res.unshift(head.val);headhead.next;}return res;…

JUnit测试Spring Service和DAO(带有内存数据库)

这篇文章描述了如何为Spring Web Application的Services和DAO实现JUnit测试。 它建立在Spring MVC-Service-DAO-Persistence Architecture Example的基础上 。 从Github的Spring-Web-JPA-Testing目录中可以找到该示例。 提醒 测试装置 –固定状态&#xff0c;用作运行测试的基…

c# 正则获取html标签内容,c# – 使用正则表达式在多个HTML标记之间获取文本

使用正则表达式,我希望能够在多个DIV标记之间获取文本.例如,以下内容&#xff1a;first html taganother tag输出&#xff1a;first html taganother tag我使用的正则表达式模式只匹配我的最后一个div标签并错过了第一个.码&#xff1a;static void Main(string[] args){string…

Android之外部存储(SD卡)

*手机的外部存储空间&#xff0c;这个我们可以理解成电脑的外接移动硬盘&#xff0c;U盘也行。所有的Android设备都有两个文件存储区域&#xff1a;“内部”和“外部”存储器。这两个名称来自早期的Android&#xff0c;当时大多数设备都提供内置的固定的内存&#xff08;内置存…

通用并发对象池

在本文中&#xff0c;我们将介绍如何在Java中创建对象池。 近年来&#xff0c;JVM的性能成倍增加&#xff0c;大多数类型的对象几乎都变得多余&#xff0c;从而提高了对象池的性能。 从本质上讲&#xff0c;对象的创建不再像以前那样昂贵。 但是&#xff0c;有些对象在创建时肯…

圆周率的代码表示,以及对其的理解。

转载的简书&#xff0c;for 记录以及记忆。 http://www.jianshu.com/p/7208e4a58310 Thanks again&#xff01; 转载于:https://www.cnblogs.com/xiapeng0701/p/7538281.html

华为NOVa8Pr0是用鸿蒙系统吗,华为Nova8即将发布,采用麒麟芯片,高端平板适配鸿蒙系统...

大家好&#xff0c;我是老孙自从华为Mate40系列发布后&#xff0c;下一步新机动态备受外界关注&#xff0c;华为究竟会不会继续生产手机呢&#xff1f;答案是肯定&#xff0c;华为Nova8系列将于本月发布&#xff0c;华为P50系列也在积极筹备&#xff0c;而且都少不了麒麟芯片&a…

使用路标的Scala和Java的Twitter REST API

如果您已阅读此博客上的其他文章&#xff0c;您可能会知道我喜欢创建各种数据集的可视化。 我刚刚开始一个小项目&#xff0c;在这里我想可视化来自Twitter的一些数据。 为此&#xff0c;我想直接从Twitter检索有关关注者的信息和个人资料信息。 我实际上开始寻找一组所有推特帐…

大话设计模式读书笔记--11.抽象工厂模式

定义 抽象工厂模式定义: 提供一个创建一系列相关或相关依赖对象的接口,而无需指定他们具体的类 抽象工厂模式通常是用于创建一族产品&#xff0c;并且这族产品分不同的等级&#xff1b;不同的具体工厂类生产不同等级的一族产品 比如下图(来源于网络) 两厢车和三厢车称为两个不同…

在线压缩html,JS代码压缩 - javascript代码压缩 - jsmin在线js压缩工具

输入代码&#xff1a;// is.js// (c) 2001 Douglas Crockford// 2001 June 3// The -is- object is used to identify the browser. Every browser edition// identifies itself, but there is no standard way of doing it, and some of// the identification is deceptive. T…

Primefaces dataTable设置某个cell的样式问题

设置primefaces dataTable的源网段列的Cell可以编辑&#xff0c;当回车键保存时&#xff0c;判断是否输入的网段合法&#xff0c;如果不合法就显示警告信息&#xff0c;并将这个不合法的数据用红色表示。问题是&#xff0c;怎么给这一个cell设定样式。通过给标签设定ID然后在后…

鸭子在Java中打字? 好吧,不完全是

根据维基百科&#xff0c;鸭子的打字是&#xff1a; 动态类型的类型&#xff0c;其中对象的方法和属性确定有效的语义&#xff0c;而不是其从特定类或特定接口的实现继承 用简单的话 当我看到一只鸟走路像鸭子&#xff0c;游泳像鸭子&#xff0c;嘎嘎像鸭子一样时&#xff0c…

前端学习路线

第一部分 HTML 第一章 职业规划和前景 职业方向规划定位&#xff1a; web前端开发工程师 web网站架构师 自己创业 转岗管理或其他 web前端开发的前景展望&#xff1a; 未来IT行业企业需求最多的人才 结合最新的html5抢占移动端的市场 自己创业做老板 随着互联网的普及we…

p1164【立方体求和】

题目&#xff1a; SubRaY有一天得到一块西瓜,是长方体形的....SubRaY发现这块西瓜长m厘米,宽n厘米,高h厘米.他发现如果把这块西瓜平均地分成m*n*h块1立方厘米的小正方体,那么每一小块都会有一个营养值(可能为负,因为西瓜是有可能坏掉的,但是绝对值不超过200).现在SubRaY决定从这…