摘要:前言在建立的過程中,往往需要對當前的測試樣例和注解進行驗證,比如檢查測試類是否含有非靜態內部類,測試類是否是的。的驗證機制非常精致而優美,在本次博客中我們就主要來談一談機制的實現。首先在中定義三個默認的類,如下。
前言
在建立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。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/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...
閱讀 2053·2021-11-11 16:55
閱讀 1395·2021-09-28 09:36
閱讀 1038·2019-08-29 15:21
閱讀 1571·2019-08-29 14:10
閱讀 2757·2019-08-29 14:08
閱讀 1628·2019-08-29 12:31
閱讀 3243·2019-08-29 12:31
閱讀 976·2019-08-26 16:47