摘要:前言在建立的过程中,往往需要对当前的测试样例和注解进行验证,比如检查测试类是否含有非静态内部类,测试类是否是的。的验证机制非常精致而优美,在本次博客中我们就主要来谈一谈机制的实现。首先在中定义三个默认的类,如下。
前言
在建立Runner的过程中,往往需要对当前的测试样例和注解进行验证,比如检查测试类是否含有非静态内部类,测试类是否是Public的。Junit的验证机制非常精致而优美,在本次博客中我们就主要来谈一谈Validator机制的实现。
指定一个验证器首先我们可以使用注解来指定某一个用户自定义Validator来进行验证,下面给出AnnotationValidator的父类以及相应注解。
@Retention(RetentionPolicy.RUNTIME) @Inherited public @interface ValidateWith { Class extends AnnotationValidator> value(); }
public abstract class AnnotationValidator { private static final ListNO_VALIDATION_ERRORS = emptyList(); public List validateAnnotatedClass(TestClass testClass) { return NO_VALIDATION_ERRORS; } public List validateAnnotatedField(FrameworkField field) { return NO_VALIDATION_ERRORS; } public List validateAnnotatedMethod(FrameworkMethod method) { return NO_VALIDATION_ERRORS; } }
以上可以很清楚地看出ValidateWith注解会指定相关的用户自定义Validator。AnnotationValidator是真正执行验证操作的单元,至于这些Validator的Validate方法在何时调用,我们会在文章的最后一部分讲解。
对一个类进行验证从上面的代码可以看出一个Validator需要实现三方面的验证——对类的验证、对方法的验证、对域的验证,Junit使用职责链模式来提供了三方面的验证。
首先在AnnotationsValidator中定义三个默认的Validator类,如下。
private static class ClassValidator extends AnnotatableValidator{ @Override Iterable getAnnotatablesForTestClass(TestClass testClass) { return singletonList(testClass); } @Override List validateAnnotatable( AnnotationValidator validator, TestClass testClass) { return validator.validateAnnotatedClass(testClass); } } private static class MethodValidator extends AnnotatableValidator { @Override Iterable getAnnotatablesForTestClass( TestClass testClass) { return testClass.getAnnotatedMethods(); } @Override List validateAnnotatable( AnnotationValidator validator, FrameworkMethod method) { return validator.validateAnnotatedMethod(method); } } private static class FieldValidator extends AnnotatableValidator { @Override Iterable getAnnotatablesForTestClass(TestClass testClass) { return testClass.getAnnotatedFields(); } @Override List validateAnnotatable( AnnotationValidator validator, FrameworkField field) { return validator.validateAnnotatedField(field); } }
然后依次调用这三种Validator
private static final List> VALIDATORS = Arrays. >asList( new ClassValidator(), new MethodValidator(), new FieldValidator()); /** * Validate all annotations of the specified test class that are be * annotated with {@link ValidateWith}. * * @param testClass * the {@link TestClass} that is validated. * @return the errors found by the validator. */ public List validateTestClass(TestClass testClass) { List validationErrors= new ArrayList (); for (AnnotatableValidator> validator : VALIDATORS) { List additionalErrors= validator .validateTestClass(testClass); validationErrors.addAll(additionalErrors); } return validationErrors; }
我们可以看到ClassValidator等都继承自AnnotatableValidator,而且在真正验证的时候调用的是它们的validateTestClass方法。它们其实也并非验证的真正执行单元,它们首先找到相应TestClass的所有对应层面的注解,然后看这些注解是否是ValidateWith类型,是的话则由类的内置工厂来提供具体的AnnotationValidator。详细情况我们在下一小节中描述。
扩展与默认的结合——漂亮的工厂首先我们给出AnnotationValidatorFactory的定义
public class AnnotationValidatorFactory { private static final ConcurrentHashMapVALIDATORS_FOR_ANNOTATION_TYPES = new ConcurrentHashMap (); public AnnotationValidator createAnnotationValidator(ValidateWith validateWithAnnotation) { AnnotationValidator validator = VALIDATORS_FOR_ANNOTATION_TYPES.get(validateWithAnnotation); if (validator != null) { return validator; } Class extends AnnotationValidator> clazz = validateWithAnnotation.value(); try { AnnotationValidator annotationValidator = clazz.newInstance(); VALIDATORS_FOR_ANNOTATION_TYPES.putIfAbsent(validateWithAnnotation, annotationValidator); return VALIDATORS_FOR_ANNOTATION_TYPES.get(validateWithAnnotation); } catch (Exception e) { throw new RuntimeException("Exception received when creating AnnotationValidator class " + clazz.getName(), e); } } }
工厂通过一个线程安全的Map存储注解和对应的实际AnnotationValidator实例,而AnnotableValidator内置通过内置一个工厂来存储所有对应层级的验证器实例,并调用这些验证器对应层级的验证方法来返回可能的异常,我们下面贴出该内部类的代码:
private static abstract class AnnotatableValidator{ private static final AnnotationValidatorFactory ANNOTATION_VALIDATOR_FACTORY = new AnnotationValidatorFactory(); abstract Iterable getAnnotatablesForTestClass(TestClass testClass); abstract List validateAnnotatable( AnnotationValidator validator, T annotatable); public List validateTestClass(TestClass testClass) { List validationErrors= new ArrayList (); for (T annotatable : getAnnotatablesForTestClass(testClass)) { List additionalErrors= validateAnnotatable(annotatable); validationErrors.addAll(additionalErrors); } return validationErrors; } private List validateAnnotatable(T annotatable) { List validationErrors= new ArrayList (); for (Annotation annotation : annotatable.getAnnotations()) { Class extends Annotation> annotationType = annotation .annotationType(); ValidateWith validateWith = annotationType .getAnnotation(ValidateWith.class); if (validateWith != null) { AnnotationValidator annotationValidator = ANNOTATION_VALIDATOR_FACTORY .createAnnotationValidator(validateWith); List errors= validateAnnotatable( annotationValidator, annotatable); validationErrors.addAll(errors); } } return validationErrors; } }
可以说这个带工厂的内部类是一处神来之笔,整个流程是AnnotationsValidator类的validateTestClass方法依次调用职责链中三个层级的AnnotatableValidator,它们先找出所有对应层次上的注解,再滤掉那些不是ValidateWith类型的注解,然后通过一个工厂来维护所有验证器实例,调用这些实例来真正验证。因为对于不同的TestClass,我们在一个层面上只用维护一个工厂。使用内部类,只暴露应该暴露的,扩展的用户只应扩展自定义的Validator,不应该在层次逻辑上进行扩展,不应该在整体验证的AnnotationsValidator之外再使用任何多带带层次的AnnotatableValidator。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/65451.html
摘要:是对测试样例的建模,用来组合多个测试样例,是中的核心内容。也是一个虚类,子类应该实现方法来决定对于是否运行。如下列代码所示组合了和,为运行时异常和断言错误屏蔽了不一致的方面,可以向上提供错误信息和样例信息。 Junit的工程结构 showImg(/img/bVsEeS); 从上图可以清楚的看出Junit大致分为几个版块,接下来一一简略介绍这些版块的作用。 runner:定义了Jun...
摘要:前言上次的博客中我们着重介绍了的机制,这次我们将聚焦到自定义扩展上来。在很多情形下我们需要在测试过程中加入一些自定义的动作,这些就需要对进行包装,为此提供了以接口和为基础的扩展机制。 前言 上次的博客中我们着重介绍了Junit的Validator机制,这次我们将聚焦到自定义扩展Rule上来。在很多情形下我们需要在测试过程中加入一些自定义的动作,这些就需要对statement进行包装,...
摘要:前言在这次的博客中我们将着重于的许多集成性功能来讨论中的种种设计模式。装饰器模式装饰器模式是为了在原有功能上加入新功能,在中绝对属于使用最频繁架构中最核心的模式,等都是通过装饰器模式来完成扩展的。 前言 在这次的博客中我们将着重于Junit的许多集成性功能来讨论Junit中的种种设计模式。可以说Junit的实现本身就是GOF设计原则的范例教本,下面就让我们开始吧。 装饰器模式 装饰器...
摘要:校验器运行完成后,会设置属性,从而计算的属性,假设校验错误,则属性值为。这样就理解了校验器的整个运行过程,也包括为何校验错误时会自动添加描述控件状态的。 我们知道,@angular/forms 包主要用来解决表单问题的,而表单问题非常重要的一个功能就是表单校验功能。数据校验非常重要,不仅仅前端在发请求给后端前需要校验数据,后端对前端发来的数据也需要校验其有效性和逻辑性,尤其在存入数据库...
摘要:的作用是包装从生成的逻辑,提供两种方案生成和。最后从生成也异常简单,也就是实现其方法返回该。 前言 尽管在第二次博客中我们讲述了Runner的运行机制,但是许多其他特性比如Filter是如何与运行流程结合却并不清楚。这次我们来回顾整理一下Junit的执行流程,给出各种特性生效的机理,并分析一些代码中精妙的地方。 Junit的执行流程 JUnitCore的RunMain方法,使用jUn...
阅读 2032·2021-11-11 16:55
阅读 1376·2021-09-28 09:36
阅读 1023·2019-08-29 15:21
阅读 1555·2019-08-29 14:10
阅读 2727·2019-08-29 14:08
阅读 1582·2019-08-29 12:31
阅读 3220·2019-08-29 12:31
阅读 951·2019-08-26 16:47