afterclass

如何允许实例方法作为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

afterclass

afterclass_通过beforeClass和afterClass设置增强Spring Test Framework相关推荐

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

    如何允许实例方法作为JUnit BeforeClass行为运行 JUnit允许您在所有测试方法调用之前和之后一次在类级别上设置方法. 但是,通过有意设计,他们将其限制为仅使用@BeforeClass和 ...

  2. Robotium编写测试用例如何模拟Junit4的BeforeClass和AfterClass方法1 - 条件判断法

    Robotium的测试类ActivityInstrumentationTestCase2是继承于Junit3的TestCase类,所以并没有提供Junit4的特性.如网上总结说的 不能通过annota ...

  3. Spring Integration Framework简介

    我们非常了解Spring框架和JMS . 在本文中,我们将介绍称为Spring Integration的企业集成框架 . Spring Integration是一个开源企业集成框架,可增强Spring ...

  4. Spring教程– Spring Core Framework教程

    Spring is one of the most widely used Java EE frameworks. I have written a lot on Spring Tutorial an ...

  5. Spring Boot Framework的关键组件和内部构造(自动装配、起步依赖、CLI、Actuator)

    Spring Boot Framework的关键组件和内部组件 在我之前的文章"Spring Boot简介"中,我们讨论了Spring Boot基础知识.现在我们将讨论" ...

  6. 如何设置Java Spring Boot JWT授权和认证

    In the past month, I had a chance to implement JWT auth for a side project. I have previously worked ...

  7. 为@Cacheable设置TTL – Spring

    今天,我被要求为应用程序正在使用的某些键设置缓存的过期时间,因此我Swift开始寻找Spring @Cacheable表示法提供的所有选项,以设置过期时间或生存时间. 由于Spring没有提供任何可配 ...

  8. 具有代理设置的Spring Cloud AWS

    在我的上一篇文章Spring和Amazon Web Services中 ,我简要介绍了Spring Cloud AWS模块以及开发人员现在对它的期望. 从官方文档中看不出来的一件事是,当您的Inter ...

  9. java 设置 cors,Spring MVC配置CORS

    Spring Framework 从4.2开始支持配置CORS. Spring MVC支持CORS的范围包括:方法级别配置CORS 全局配置CORS 方法级别配置CORS 使用注解@CrossOrgi ...

最新文章

  1. 超大福利 | 这款免费 Java 在线诊断利器,不用真的会后悔!
  2. loadrunner支持php包吗,Loadrunner自带的WebTours
  3. nginx 实现Web应用程序的负载均衡
  4. linux mp4v2编译,Android 编译mp4 v2 2.0.0生成动态库
  5. Ajax 跨域,这应该是最全的解决方案了
  6. 返回零长度的数组或者集合,而不是null
  7. 搭建Windows SVN服务器及TortoiseSVN使用帮助和下载
  8. oppor829t如何刷机_OPPO R829T中文Recovery刷机教程
  9. 金盾加密视频提取,真实机器码在这里
  10. su灯光插件_V-Ray for SketchUp渲染外部照明快速入门
  11. 密码编码学与网络安全学习笔记
  12. 【pytorch】Conv2d()里面的参数bias什么时候加,什么时候不加?
  13. 【强化学习】DQN:Flappy Bird实例分析
  14. 如何成为技术大牛--摘自牛人
  15. 今日头条自动开宝箱脚本
  16. 解决:香橙派orangepi3lts网口用不了 网口灯不亮 没反应
  17. C#与产电PLC以太网通讯,C# For LS PLC Ethernet Communication,产电PLC以太网通讯,上位机与PLC通讯C#,LG PLC以太网通讯
  18. 【自适应波束形成】MVDR(Minimum Variance Distortionless Response )笔记
  19. Z05 - 028、分析模型
  20. 浏览器数据库 IndexedDB 介绍

热门文章

  1. 以吃货的角度理解 IaaS,PaaS,SaaS 是什么
  2. 阿里巴巴对Java编程【控制语句】的规约
  3. Java List面试题汇总
  4. 装饰器模式和代理模式的区别
  5. 日常技术分享 : 一定要注意replcaceAll方法,有时候会如你所不愿!
  6. 1-10 之间的整数相加,得到累加值大于 20 的当前数
  7. 第四章选择结构(二)
  8. 人脸登陆facelogin
  9. HBase体系架构说明
  10. vue 多页面多模块分模块打包 分插件安装_Vue渲染方式