摘要:開發(fā)原因是一個非常小的類庫,通過代碼生成來提供高性能的反射處理,自動為字段提供訪問類,訪問類使用字節(jié)碼操作而不是的反射技術(shù),因此非???。
ReflectASM 是一個非常小的 Java 類庫,通過代碼生成來提供高性能的反射處理,自動為 get/set 字段提供訪問類,訪問類使用字節(jié)碼操作而不是 Java 的反射技術(shù),因此非常快。
在單元測試的時,需要模擬制造一些數(shù)據(jù)去測試我們代碼會不會出現(xiàn)明顯的異常(字段導(dǎo)致、空指針),除了自己亂編寫一些測試數(shù)據(jù)以外,也會實(shí)用javafaker進(jìn)行“真實(shí)數(shù)據(jù)”模擬,當(dāng)然可以減少我們的代碼量。如果,只是前期的簡單測試,對數(shù)據(jù)的正確性并不高,使用javafaker對每個字段都進(jìn)行編造,代碼量還是不少,于是,這個時候,F(xiàn)akeDataMaker就這么誕生了,一個基于javafaker和ReflectASM工具“亂”填充數(shù)據(jù)的測試工具
這個類是整個工具的一個基類,定義了各種類型的參數(shù)的抽象方法,基本上包括常用的基類,又加上了Date、Boolean、BigDecimal類型
三種類型,這里入?yún)?strong>String fieldName,雖然工具可以亂生成數(shù)據(jù),但是,要是有些字段還是需要特殊化的處理,便可以重寫這些方法,根據(jù)傳入的字段名稱,進(jìn)行數(shù)據(jù)自定義填充,比如
FakeDataMaker onlyFieldOne = new FakeDataMaker(){ @Override protected String makeString(String fieldName) { if("data3".equals(fieldName)){ return "zjjdjd"; } return super.makeString(fieldName); } };
這樣便可以自定義data3字段,固定為“zjjdjd”
這是這個工具中最重要的類,繼承了AbstractFakeDataMaker方法,并實(shí)現(xiàn)了幾個造數(shù)據(jù)的方法,目前這個類可以實(shí)現(xiàn)兩個功能,一個是填充數(shù)據(jù),一個是構(gòu)造空值的對象
調(diào)用makeData方法便可以得到一個填充完數(shù)據(jù)的對象,有兩個makeData方法,默認(rèn)自定義faker的字符集為中文,也支持自定義字符集
public final Object makeData(Class testClass) { return makeData(testClass, Locale.CHINA);}
makeData方法中使用了很多緩存,便于遍歷生成數(shù)據(jù)的速度
//構(gòu)建對象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); }····· //獲取到對應(yīng)的get方法的下標(biāo)緩存 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,方便構(gòu)建對象和獲取到方法的句柄,從而進(jìn)行賦值,這里為什么會引入ReflectASM工具,因?yàn)?,我在工作開發(fā)中涉及到一個字段映射的功能實(shí)現(xiàn),使用ReflectASM+注解的方法可以減少大量代碼,所以在測試過程中,乘熱打鐵就ReflectASM工具,不過,主要還是ReflectASM工具真的比較好用
由于我同事第一次使用FakeDataMaker工具就直接使用了基類進(jìn)行了賦值,然后就報錯了,所以對于傳入基類的賦值做了點(diǎn)特殊處理
//基礎(chǔ)類型的參數(shù)構(gòu)建 if (baseClass(testClass)) { if (WARNING_FLAG) { System.err.println("建議直接使用Java—faker生成基礎(chǔ)類數(shù)據(jù)"); WARNING_FLAG = false; } return baseClassValue(testClass, new Object()); }
雖然,不建議對基類進(jìn)行賦值,使用Java faker會比較更方便,為了避免異常,對主要的幾種基類數(shù)據(jù)填充
FakeDataMaker util = new FakeDataMaker();Integer testData = (Integer) util.makeData(Integer.class);System.out.println(testData);建議直接使用Java—faker生成基礎(chǔ)類數(shù)據(jù)87
baseClassValue方法還是調(diào)用了內(nèi)部的賦值方法
如果傳入的類,存在內(nèi)部類,這里需要setInnerFlag調(diào)用方法開啟對內(nèi)部類的支持
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); }}
內(nèi)部類的數(shù)據(jù)填充,其實(shí)是一個遞歸調(diào)用makeData
接下來是核心部分,根據(jù)字段的數(shù)據(jù)類型,進(jìn)行賦值操作,這里就使用到了重寫的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));}
由于只是簡單的功能測試的數(shù)據(jù)填充,在數(shù)據(jù)的準(zhǔn)確性上默認(rèn)方法還是有很大的偏差的,所以,我在實(shí)際的使用過程中,大部分還是重寫了造值的各種方法,比如ID字段,就返回uuid,字符的時間、數(shù)字等
FakeDataMaker stringOnlyOne = new FakeDataMaker() { @Override protected String makeString(String fieldName) { if ("id".equals(fieldName)) { return "1231231231231321"; } return super.makeString(fieldName); }};
這也是在我同事的代碼中遇到的一個問題,從而想到的一個積極方案,比如一個統(tǒng)計(jì)類的VO,會有很多統(tǒng)計(jì)的數(shù)值屬性,能查到結(jié)果的就會賦值,沒有結(jié)果的就默認(rèn)0,如果使用賦值方法則需要把全部的屬性都要賦值默認(rèn)值0,或者調(diào)用構(gòu)造方法賦值,兩種方法都會有大量的代碼,不太簡潔,尤其使用構(gòu)造方法,會有很多的入?yún)ⅲ褂?strong>FakeDataMaker一句代碼就可以完成空值對象的賦值
TestData testData1 = (TestData) FakeDataMaker.initEmptyObject(TestData.class);
實(shí)際效果可以看到,字符類型默認(rèn)為“”,數(shù)字值則是0或者0.0,時間則默認(rèn)為當(dāng)前時間
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(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)]
空值構(gòu)建默認(rèn)只支持對內(nèi)部類進(jìn)行值填充的,可以調(diào)用 initEmptyObject(Class testClass, Boolean innerFlag) 關(guān)閉對內(nèi)部類的填充
initEmptyObject方法內(nèi)部是重寫了FakeDataMaker方法,重寫造值方法,為空值,FakeDataMaker內(nèi)部也緩存了一個FakeDataMaker對象,便于重復(fù)的調(diào)用
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) { //這里沒設(shè)置緩存 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);
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/122269.html
摘要:模式,單實(shí)例多進(jìn)程,常用于多語言混編,比如等,不支持端口復(fù)用,需要自己做應(yīng)用的端口分配和負(fù)載均衡的子進(jìn)程業(yè)務(wù)代碼。就是我們需要一個調(diào)度者,保證所有后端服務(wù)器都將性能充分發(fā)揮,從而保持服務(wù)器集群的整體性能最優(yōu),這就是負(fù)載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
摘要:模式,單實(shí)例多進(jìn)程,常用于多語言混編,比如等,不支持端口復(fù)用,需要自己做應(yīng)用的端口分配和負(fù)載均衡的子進(jìn)程業(yè)務(wù)代碼。就是我們需要一個調(diào)度者,保證所有后端服務(wù)器都將性能充分發(fā)揮,從而保持服務(wù)器集群的整體性能最優(yōu),這就是負(fù)載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
摘要:模式,單實(shí)例多進(jìn)程,常用于多語言混編,比如等,不支持端口復(fù)用,需要自己做應(yīng)用的端口分配和負(fù)載均衡的子進(jìn)程業(yè)務(wù)代碼。就是我們需要一個調(diào)度者,保證所有后端服務(wù)器都將性能充分發(fā)揮,從而保持服務(wù)器集群的整體性能最優(yōu),這就是負(fù)載均衡。 showImg(https://segmentfault.com/img/remote/1460000019425391?w=1440&h=1080); Nod...
閱讀 2330·2021-11-23 10:09
閱讀 2898·2021-10-12 10:11
閱讀 2604·2021-09-29 09:35
閱讀 1345·2019-08-30 15:53
閱讀 2271·2019-08-30 11:15
閱讀 2916·2019-08-29 13:01
閱讀 2299·2019-08-28 18:15
閱讀 3369·2019-08-26 12:13