摘要:開發(fā)原因是一個非常小的類庫,通過代碼生成來提供高性能的反射處理,自動為字段提供訪問類,訪問類使用字節(jié)碼操作而不是的反射技術,因此非常快。
ReflectASM 是一個非常小的 Java 類庫,通過代碼生成來提供高性能的反射處理,自動為 get/set 字段提供訪問類,訪問類使用字節(jié)碼操作而不是 Java 的反射技術,因此非常快。
在單元測試的時,需要模擬制造一些數據去測試我們代碼會不會出現明顯的異常(字段導致、空指針),除了自己亂編寫一些測試數據以外,也會實用javafaker進行“真實數據”模擬,當然可以減少我們的代碼量。如果,只是前期的簡單測試,對數據的正確性并不高,使用javafaker對每個字段都進行編造,代碼量還是不少,于是,這個時候,FakeDataMaker就這么誕生了,一個基于javafaker和ReflectASM工具“亂”填充數據的測試工具
這個類是整個工具的一個基類,定義了各種類型的參數的抽象方法,基本上包括常用的基類,又加上了Date、Boolean、BigDecimal類型
三種類型,這里入參為String fieldName,雖然工具可以亂生成數據,但是,要是有些字段還是需要特殊化的處理,便可以重寫這些方法,根據傳入的字段名稱,進行數據自定義填充,比如
FakeDataMaker onlyFieldOne = new FakeDataMaker(){ @Override protected String makeString(String fieldName) { if("data3".equals(fieldName)){ return "zjjdjd"; } return super.makeString(fieldName); } };
這樣便可以自定義data3字段,固定為“zjjdjd”
這是這個工具中最重要的類,繼承了AbstractFakeDataMaker方法,并實現了幾個造數據的方法,目前這個類可以實現兩個功能,一個是填充數據,一個是構造空值的對象
調用makeData方法便可以得到一個填充完數據的對象,有兩個makeData方法,默認自定義faker的字符集為中文,也支持自定義字符集
public final Object makeData(Class testClass) { return makeData(testClass, Locale.CHINA);}
makeData方法中使用了很多緩存,便于遍歷生成數據的速度
//構建對象ConstructorAccess constructorAccess = CONSTRUCTOR_ACCESS_MAP.get(testClass);if (constructorAccess == null) { constructorAccess = ConstructorAccess.get(testClass); CONSTRUCTOR_ACCESS_MAP.put(testClass, constructorAccess);}·····//類方法的緩存MethodAccess testAccess = METHOD_ACCESS_MAP.get(testClass); if (testAccess == null) { testAccess = MethodAccess.get(testClass); METHOD_ACCESS_MAP.put(testClass, testAccess); }····· //字段的緩存 List<Field> fields = FIELDS_MAP.get(testClass); if (fields == null) { fields = getAllFields(testClass); FIELDS_MAP.put(testClass, fields); }····· //獲取到對應的get方法的下標緩存 Integer set_index = INDEX_MAP.get(get_key); if (set_index == null) { set_index = testAccess.getIndex(GET_METHOD + StringUtils.capitalize(field.getName())); INDEX_MAP.put(get_key, set_index);}
這里使用了ReflectASM工具的ConstructorAccess、MethodAccess,方便構建對象和獲取到方法的句柄,從而進行賦值,這里為什么會引入ReflectASM工具,因為,我在工作開發(fā)中涉及到一個字段映射的功能實現,使用ReflectASM+注解的方法可以減少大量代碼,所以在測試過程中,乘熱打鐵就ReflectASM工具,不過,主要還是ReflectASM工具真的比較好用
由于我同事第一次使用FakeDataMaker工具就直接使用了基類進行了賦值,然后就報錯了,所以對于傳入基類的賦值做了點特殊處理
//基礎類型的參數構建 if (baseClass(testClass)) { if (WARNING_FLAG) { System.err.println("建議直接使用Java—faker生成基礎類數據"); WARNING_FLAG = false; } return baseClassValue(testClass, new Object()); }
雖然,不建議對基類進行賦值,使用Java faker會比較更方便,為了避免異常,對主要的幾種基類數據填充
FakeDataMaker util = new FakeDataMaker();Integer testData = (Integer) util.makeData(Integer.class);System.out.println(testData);建議直接使用Java—faker生成基礎類數據87
baseClassValue方法還是調用了內部的賦值方法
如果傳入的類,存在內部類,這里需要setInnerFlag調用方法開啟對內部類的支持
if (INNER_FLAG) { Boolean baseValue = BASE_MAP.get(get_key); if (baseValue == null) { baseValue = !baseClass(field.getType()) && !field.getType().equals(testClass) && Modifier.toString(field.getType().getModifiers()).contains("static"); BASE_MAP.put(get_key, baseValue); } if (baseValue) { Object baseClassValue = makeData(field.getType(), locale); testAccess.invoke(testObject, set_index, baseClassValue); }}
內部類的數據填充,其實是一個遞歸調用makeData
接下來是核心部分,根據字段的數據類型,進行賦值操作,這里就使用到了重寫的AbstractFakeDataMaker**定義的幾個造值方法
if (field.getType() == String.class) { testAccess.invoke(testObject, set_index, makeString(field.getName()));}if (field.getType() == Integer.class || field.getType() == int.class) { testAccess.invoke(testObject, set_index, makeInteger(field.getName()));}if (field.getType() == Float.class || field.getType() == float.class) { testAccess.invoke(testObject, set_index, makeFloat(field.getName()));}if (field.getType() == Double.class || field.getType() == double.class) { testAccess.invoke(testObject, set_index, makeDouble(field.getName()));}if (field.getType() == Long.class || field.getType() == long.class) { testAccess.invoke(testObject, set_index, makeLong(field.getName()));}if (field.getType() == Date.class) { testAccess.invoke(testObject, set_index, makeDate(field.getName()));}if (field.getType() == Boolean.class || field.getType() == boolean.class) { testAccess.invoke(testObject, set_index, makeBoolean(field.getName()));}if (field.getType() == BigDecimal.class) { testAccess.invoke(testObject, set_index, makeBigDecimal(field.getName()));}
目前,字符型,填充的是,faker的name類型的字段
@Overrideprotected String makeString(String fieldName) { return faker.name().fullName();}@Overrideprotected Integer makeInteger(String fieldName) { return faker.number().numberBetween(1, 100);}@Overrideprotected Float makeFloat(String fieldName) { Double randomDouble = faker.number().randomDouble(2, 1, 100); return randomDouble.floatValue();}@Overrideprotected Double makeDouble(String fieldName) { return faker.number().randomDouble(2, 1, 100);}@Overrideprotected Long makeLong(String fieldName) { Double randomDouble = faker.number().randomDouble(2, 1, 100); return randomDouble.longValue();}@Overrideprotected Date makeDate(String fieldName) { return faker.date().birthday();}@Overrideprotected Boolean makeBoolean(String fieldName) { if (faker.number().numberBetween(0, 2) == 1) { return Boolean.TRUE; } return Boolean.FALSE;}@Overrideprotected BigDecimal makeBigDecimal(String fieldName) { Double randomDouble = faker.number().randomDouble(2, 1, 1000); return new BigDecimal(String.valueOf(randomDouble));}
由于只是簡單的功能測試的數據填充,在數據的準確性上默認方法還是有很大的偏差的,所以,我在實際的使用過程中,大部分還是重寫了造值的各種方法,比如ID字段,就返回uuid,字符的時間、數字等
FakeDataMaker stringOnlyOne = new FakeDataMaker() { @Override protected String makeString(String fieldName) { if ("id".equals(fieldName)) { return "1231231231231321"; } return super.makeString(fieldName); }};
這也是在我同事的代碼中遇到的一個問題,從而想到的一個積極方案,比如一個統計類的VO,會有很多統計的數值屬性,能查到結果的就會賦值,沒有結果的就默認0,如果使用賦值方法則需要把全部的屬性都要賦值默認值0,或者調用構造方法賦值,兩種方法都會有大量的代碼,不太簡潔,尤其使用構造方法,會有很多的入參,使用FakeDataMaker一句代碼就可以完成空值對象的賦值
TestData testData1 = (TestData) FakeDataMaker.initEmptyObject(TestData.class);
實際效果可以看到,字符類型默認為“”,數字值則是0或者0.0,時間則默認為當前時間
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-F6DTyUik-1633882390009)(https://www.kura.ren/upload/2021/10/%E6%88%AA%E5%B1%8F2021-10-11%20%E4%B8%8A%E5%8D%8812.00.38-0dbbc7a502de44dba717a274079694e4.png)]
空值構建默認只支持對內部類進行值填充的,可以調用 initEmptyObject(Class testClass, Boolean innerFlag) 關閉對內部類的填充
initEmptyObject方法內部是重寫了FakeDataMaker方法,重寫造值方法,為空值,FakeDataMaker內部也緩存了一個FakeDataMaker對象,便于重復的調用
if (EMPTY_OBJECT_MAKER == null || !EMPTY_OBJECT_MAKER.INNER_FLAG.equals(innerFlag)) { EMPTY_OBJECT_MAKER = new FakeDataMaker() { @Override protected String makeString(String fieldName) { return ""; } @Override protected Integer makeInteger(String fieldName) { return 0; } @Override protected Float makeFloat(String fieldName) { return 0F; } @Override protected Double makeDouble(String fieldName) { return 0D; } @Override protected Long makeLong(String fieldName) { return 0L; } @Override protected Date makeDate(String fieldName) { //這里沒設置緩存 return new Date(); } @Override protected Boolean makeBoolean(String fieldName) { return Boolean.TRUE; } @Override protected BigDecimal makeBigDecimal(String fieldName) { return BigDecimal.ZERO; } }; EMPTY_OBJECT_MAKER.setInnerFlag(innerFlag);}return EMPTY_OBJECT_MAKER.makeData(testClass);
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/122269.html
摘要:模式,單實例多進程,常用于多語言混編,比如等,不支持端口復用,需要自己做應用的端口分配和負載均衡的子進程業(yè)務代碼。就是我們需要一個調度者,保證所有后端服務器都將性能充分發(fā)揮,從而保持服務器集群的整體性能最優(yōu),這就是負載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
摘要:模式,單實例多進程,常用于多語言混編,比如等,不支持端口復用,需要自己做應用的端口分配和負載均衡的子進程業(yè)務代碼。就是我們需要一個調度者,保證所有后端服務器都將性能充分發(fā)揮,從而保持服務器集群的整體性能最優(yōu),這就是負載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
摘要:模式,單實例多進程,常用于多語言混編,比如等,不支持端口復用,需要自己做應用的端口分配和負載均衡的子進程業(yè)務代碼。就是我們需要一個調度者,保證所有后端服務器都將性能充分發(fā)揮,從而保持服務器集群的整體性能最優(yōu),這就是負載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
閱讀 2316·2021-11-23 10:09
閱讀 2891·2021-10-12 10:11
閱讀 2599·2021-09-29 09:35
閱讀 1342·2019-08-30 15:53
閱讀 2266·2019-08-30 11:15
閱讀 2909·2019-08-29 13:01
閱讀 2295·2019-08-28 18:15
閱讀 3367·2019-08-26 12:13