自定义注解过滤JUnit测试类
来源:互联网 发布:linux中man的用法 编辑:程序博客网 时间:2024/06/13 01:28
JUnit是常用单元测试工具,如果希望跳过某个测试类,一般在类上面添加@Ignore注解。实际情况下,经常遇到某些测试类在符合某些条件时需要运行、不符合时又不需要运行的情况,频繁加减@Ignore注解的话相当繁琐。有没有办法,能根据自己的配置文件,灵活决定是否运行某些测试类呢?
首先来分析一下JUnit源码(以4.10版本为例)。在org.junit.runner包下,有个JUnitCore.class,其中的main方法就是JUnit入口函数。经过runMainAndExit->runMain->run的多次调用,发现在run之中通过Request.classes方法构建了AllDefaultPossibilitiesBuilder对象,该对象用于选择RunnerBuilder,继而选择Runner执行测试用例。源码如下:
public static Request classes(Computer computer, Class<?>... classes) {try {AllDefaultPossibilitiesBuilder builder= new AllDefaultPossibilitiesBuilder(true);Runner suite= computer.getSuite(builder, classes);return runner(suite);} catch (InitializationError e) {throw new RuntimeException("Bug in saff's brain: Suite constructor, called as above, should always complete");}}
在AllDefaultPossibilitiesBuilder中有个runnerForClass方法,就是该方法选择了RunnerBuilder,并通过调用RunnerBuilder的runnerForClass方法,最终决定了Runner:
@Overridepublic Runner runnerForClass(Class<?> testClass) throws Throwable {List<RunnerBuilder> builders= Arrays.asList(ignoredBuilder(),annotatedBuilder(),suiteMethodBuilder(),junit3Builder(),junit4Builder());for (RunnerBuilder each : builders) {Runner runner= each.safeRunnerForClass(testClass);if (runner != null)return runner;}return null;}
从上述代码可以看出,正常情况下会选择JUnit4Builder,其源码如下:
public class JUnit4Builder extends RunnerBuilder {@Overridepublic Runner runnerForClass(Class<?> testClass) throws Throwable {return new BlockJUnit4ClassRunner(testClass);}}
而一旦对类添加了@Ignore注解,则会选择IgnoredBuilder,其源码如下:
public class IgnoredBuilder extends RunnerBuilder {@Overridepublic Runner runnerForClass(Class<?> testClass) {if (testClass.getAnnotation(Ignore.class) != null)return new IgnoredClassRunner(testClass);return null;}}
看到这里,我们大体可以认为,BlockJUnit4ClassRunner(testClass)会正常运行测试类,而IgnoredClassRunner(testClass)则会跳过运行测试类。因此,对于开始提出的问题,可以用如下方法解决:构建自己的Builder类,在其中的runnerForClass中根据配置决定是否运行测试类。
由于IgnoredBuilder中是通过读取类注解的方法,我们不妨类似定义自己的Ignore注解:
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD, ElementType.TYPE })public @interface MyIgnore {String value() default "";}
然后,新建与JUnit源码中同名的包org.junit.internal.builders,并在其中新建同名文件AllDefaultPossibilitiesBuilder,拷贝进来JUnit同名文件的源码,并修改/添加其中如下部分:
@Overridepublic Runner runnerForClass(Class<?> testClass) throws Throwable {List<RunnerBuilder> builders= Arrays.asList(ignoredBuilder(),myBuilder(),annotatedBuilder(),suiteMethodBuilder(),junit3Builder(),junit4Builder());for (RunnerBuilder each : builders) {Runner runner= each.safeRunnerForClass(testClass);if (runner != null)return runner;}return null;}
protected MyBuilder myBuilder() {return new MyBuilder();}
并在这个包下,定义自己的Builder:
public class MyBuilder extends RunnerBuilder {@Overridepublic Runner runnerForClass(Class<?> testClass) throws Throwable {if (testClass.getAnnotation(MyIgnore.class) != null) {if (...) //自定义过滤条件return new IgnoredClassRunner(testClass);}return new BlockJUnit4ClassRunner(testClass);}}
这样,只要在测试类上添加自定义注解@MyIgnore,即可根据自定义过滤条件决定该类运行与否。
PS:编写测试类时,一般是在基类中读取配置文件,并为某些成员赋值。而在MyBuilder中传入基类的成员变量作为过滤条件是不行的,因为MyBuilder的运行还在测试基类之前,此时基类的任何变量都为null。在MyBuilder中自定义过滤条件的变量,必须在MyBuilder中从配置文件里实时读取。由于基类也需要加载配置文件,为避免重复加载,建议将读配置类实现为单例模式:
public class MyConfiguration {private volatile static MyConfiguration uniqueInstance;private Properties propertie;private FileInputStream inputFile;private MyConfiguration() {propertie = new Properties();}private MyConfiguration(String configFile) {propertie = new Properties();try {inputFile = new FileInputStream(configFile);propertie.load(inputFile);inputFile.close();} catch (FileNotFoundException ex) {System.out.println("load properties file failed! maybe file not exist!");ex.printStackTrace();} catch (IOException ex) {System.out.println("load properties file failed!");ex.printStackTrace();}}/** * @return unique instance */public static MyConfiguration getInstance(String filePath) {if (uniqueInstance == null) {synchronized (MyConfiguration.class) {if (uniqueInstance == null) {uniqueInstance = new MyConfiguration(filePath);}}}return uniqueInstance;}public String getValue(String key) {if (propertie.containsKey(key)) {String value = propertie.getProperty(key);return value;} elsereturn "";}}
相应的,MyBuilder可修改为:
public class MyBuilder extends RunnerBuilder {@Overridepublic Runner runnerForClass(Class<?> testClass) throws Throwable {String properties = "src/test/resources/test.properties";MyConfiguration conf = MyConfiguration.getInstance(properties);if (testClass.getAnnotation(MyIgnore.class) != null) {if (!"run".equals(conf.getValue("runFlag")))return new IgnoredClassRunner(testClass);}return new BlockJUnit4ClassRunner(testClass);}}
- 自定义注解过滤JUnit测试类
- junit测试@注解
- java自定义 注解 annotation、标签库tag、监听listener、junit简单测试代码
- 使用注解实现自定义junit(简)
- 基于spring 3 注解的junit测试
- Junit注解方式测试Web服务
- JUnit(二) 测试结果和注解
- 用Junit测试出现映射注解异常
- JUnit测试技术 和 java注解
- 创建JUNIT测试类
- JUnit测试类
- JUnit 测试类
- Junit测试类BUG
- junit 测试类
- 利用类反射和注解做自己的JUnit测试工具
- EffectiveJava(35) -- 注解优先于命名模式(从零构建JUnit测试类)
- Junit注解
- junit注解
- 看看自己楼下的猫好奇感啊
- hdu 4738 Caocao's Bridges(求割边和桥)
- 2013华为校园招聘上机题 C++ 练习
- kobject和kset .
- 主项定理Master Method
- 自定义注解过滤JUnit测试类
- 总线设备模型-kobject .
- LeetCode | Same Tree
- android_1
- ifstream文件尾最后一行读两次
- 在编译内核的最后阶段出现sdhci_esdhc_imx_pdata未定义的错误
- linux系统编译C++程序时头文件和库文件搜索路径
- 输入四个点的坐标,求证四个点是不是一个矩形
- mips64高精度时钟引起ktime_get时间不准,导致饿狗故障原因分析