成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

項(xiàng)目工具:兩行代碼快速生成測試的數(shù)據(jù)的FakeDataMaker

X_AirDu / 2897人閱讀

摘要:開發(fā)原因是一個非常小的類庫,通過代碼生成來提供高性能的反射處理,自動為字段提供訪問類,訪問類使用字節(jié)碼操作而不是的反射技術(shù),因此非???。

開發(fā)原因

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ù)的測試工具

UML圖

源碼分析

AbstractFakeDataMaker

這個類是整個工具的一個基類,定義了各種類型的參數(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”

FakeDataMaker

這是這個工具中最重要的類,繼承了AbstractFakeDataMaker方法,并實(shí)現(xiàn)了幾個造數(shù)據(jù)的方法,目前這個類可以實(shí)現(xiàn)兩個功能,一個是填充數(shù)據(jù),一個是構(gòu)造空值的對象

填充數(shù)據(jù)

調(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);    }};

構(gòu)造空值

這也是在我同事的代碼中遇到的一個問題,從而想到的一個積極方案,比如一個統(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);

源碼地址

https://github.com/liuhao192/FakerTestDataMakeUtil

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/122269.html

相關(guān)文章

  • Node.js運(yùn)行原理、高并發(fā)性能測試對比及生態(tài)圈匯總

    摘要:模式,單實(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...

    kamushin233 評論0 收藏0
  • Node.js運(yùn)行原理、高并發(fā)性能測試對比及生態(tài)圈匯總

    摘要:模式,單實(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...

    BDEEFE 評論0 收藏0
  • Node.js運(yùn)行原理、高并發(fā)性能測試對比及生態(tài)圈匯總

    摘要:模式,單實(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...

    TesterHome 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<