摘要:前言在建立的過程中,往往需要對當(dāng)前的測試樣例和注解進(jìn)行驗(yàn)證,比如檢查測試類是否含有非靜態(tài)內(nèi)部類,測試類是否是的。的驗(yàn)證機(jī)制非常精致而優(yōu)美,在本次博客中我們就主要來談一談機(jī)制的實(shí)現(xiàn)。首先在中定義三個默認(rèn)的類,如下。
前言
在建立Runner的過程中,往往需要對當(dāng)前的測試樣例和注解進(jìn)行驗(yàn)證,比如檢查測試類是否含有非靜態(tài)內(nèi)部類,測試類是否是Public的。Junit的驗(yàn)證機(jī)制非常精致而優(yōu)美,在本次博客中我們就主要來談一談Validator機(jī)制的實(shí)現(xiàn)。
指定一個驗(yàn)證器首先我們可以使用注解來指定某一個用戶自定義Validator來進(jìn)行驗(yàn)證,下面給出AnnotationValidator的父類以及相應(yīng)注解。
@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注解會指定相關(guān)的用戶自定義Validator。AnnotationValidator是真正執(zhí)行驗(yàn)證操作的單元,至于這些Validator的Validate方法在何時調(diào)用,我們會在文章的最后一部分講解。
對一個類進(jìn)行驗(yàn)證從上面的代碼可以看出一個Validator需要實(shí)現(xiàn)三方面的驗(yàn)證——對類的驗(yàn)證、對方法的驗(yàn)證、對域的驗(yàn)證,Junit使用職責(zé)鏈模式來提供了三方面的驗(yàn)證。
首先在AnnotationsValidator中定義三個默認(rèn)的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); } }
然后依次調(diào)用這三種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,而且在真正驗(yàn)證的時候調(diào)用的是它們的validateTestClass方法。它們其實(shí)也并非驗(yàn)證的真正執(zhí)行單元,它們首先找到相應(yīng)TestClass的所有對應(yīng)層面的注解,然后看這些注解是否是ValidateWith類型,是的話則由類的內(nèi)置工廠來提供具體的AnnotationValidator。詳細(xì)情況我們在下一小節(jié)中描述。
擴(kuò)展與默認(rèn)的結(jié)合——漂亮的工廠首先我們給出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存儲注解和對應(yīng)的實(shí)際AnnotationValidator實(shí)例,而AnnotableValidator內(nèi)置通過內(nèi)置一個工廠來存儲所有對應(yīng)層級的驗(yàn)證器實(shí)例,并調(diào)用這些驗(yàn)證器對應(yīng)層級的驗(yàn)證方法來返回可能的異常,我們下面貼出該內(nèi)部類的代碼:
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; } }
可以說這個帶工廠的內(nèi)部類是一處神來之筆,整個流程是AnnotationsValidator類的validateTestClass方法依次調(diào)用職責(zé)鏈中三個層級的AnnotatableValidator,它們先找出所有對應(yīng)層次上的注解,再濾掉那些不是ValidateWith類型的注解,然后通過一個工廠來維護(hù)所有驗(yàn)證器實(shí)例,調(diào)用這些實(shí)例來真正驗(yàn)證。因?yàn)閷τ诓煌腡estClass,我們在一個層面上只用維護(hù)一個工廠。使用內(nèi)部類,只暴露應(yīng)該暴露的,擴(kuò)展的用戶只應(yīng)擴(kuò)展自定義的Validator,不應(yīng)該在層次邏輯上進(jìn)行擴(kuò)展,不應(yīng)該在整體驗(yàn)證的AnnotationsValidator之外再使用任何多帶帶層次的AnnotatableValidator。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65451.html
摘要:是對測試樣例的建模,用來組合多個測試樣例,是中的核心內(nèi)容。也是一個虛類,子類應(yīng)該實(shí)現(xiàn)方法來決定對于是否運(yùn)行。如下列代碼所示組合了和,為運(yùn)行時異常和斷言錯誤屏蔽了不一致的方面,可以向上提供錯誤信息和樣例信息。 Junit的工程結(jié)構(gòu) showImg(/img/bVsEeS); 從上圖可以清楚的看出Junit大致分為幾個版塊,接下來一一簡略介紹這些版塊的作用。 runner:定義了Jun...
摘要:前言上次的博客中我們著重介紹了的機(jī)制,這次我們將聚焦到自定義擴(kuò)展上來。在很多情形下我們需要在測試過程中加入一些自定義的動作,這些就需要對進(jìn)行包裝,為此提供了以接口和為基礎(chǔ)的擴(kuò)展機(jī)制。 前言 上次的博客中我們著重介紹了Junit的Validator機(jī)制,這次我們將聚焦到自定義擴(kuò)展Rule上來。在很多情形下我們需要在測試過程中加入一些自定義的動作,這些就需要對statement進(jìn)行包裝,...
摘要:前言在這次的博客中我們將著重于的許多集成性功能來討論中的種種設(shè)計(jì)模式。裝飾器模式裝飾器模式是為了在原有功能上加入新功能,在中絕對屬于使用最頻繁架構(gòu)中最核心的模式,等都是通過裝飾器模式來完成擴(kuò)展的。 前言 在這次的博客中我們將著重于Junit的許多集成性功能來討論Junit中的種種設(shè)計(jì)模式??梢哉fJunit的實(shí)現(xiàn)本身就是GOF設(shè)計(jì)原則的范例教本,下面就讓我們開始吧。 裝飾器模式 裝飾器...
摘要:校驗(yàn)器運(yùn)行完成后,會設(shè)置屬性,從而計(jì)算的屬性,假設(shè)校驗(yàn)錯誤,則屬性值為。這樣就理解了校驗(yàn)器的整個運(yùn)行過程,也包括為何校驗(yàn)錯誤時會自動添加描述控件狀態(tài)的。 我們知道,@angular/forms 包主要用來解決表單問題的,而表單問題非常重要的一個功能就是表單校驗(yàn)功能。數(shù)據(jù)校驗(yàn)非常重要,不僅僅前端在發(fā)請求給后端前需要校驗(yàn)數(shù)據(jù),后端對前端發(fā)來的數(shù)據(jù)也需要校驗(yàn)其有效性和邏輯性,尤其在存入數(shù)據(jù)庫...
摘要:的作用是包裝從生成的邏輯,提供兩種方案生成和。最后從生成也異常簡單,也就是實(shí)現(xiàn)其方法返回該。 前言 盡管在第二次博客中我們講述了Runner的運(yùn)行機(jī)制,但是許多其他特性比如Filter是如何與運(yùn)行流程結(jié)合卻并不清楚。這次我們來回顧整理一下Junit的執(zhí)行流程,給出各種特性生效的機(jī)理,并分析一些代碼中精妙的地方。 Junit的執(zhí)行流程 JUnitCore的RunMain方法,使用jUn...
閱讀 2072·2021-11-11 16:55
閱讀 1408·2021-09-28 09:36
閱讀 1050·2019-08-29 15:21
閱讀 1582·2019-08-29 14:10
閱讀 2767·2019-08-29 14:08
閱讀 1642·2019-08-29 12:31
閱讀 3253·2019-08-29 12:31
閱讀 985·2019-08-26 16:47