Java中如何生成隨機假數據對象

前言

想要生成一些有些隨機性的Javabean對象用來測試,又想讓他們看上去像是真的?
想要一些大量的Javabean用來做測試,但是手動編寫太累了?
想要用一套模板來定義一個Javabean的字段隨機規則?
啥也不想,就進來看看?
如果你有以上想法或者需求,你可以試試一個叫做 Mock.java 的輕量級假數據生成框架,或者說工具。

github地址:https://github.com/ForteScarlet/Mock.java
gitee地址:https://gitee.com/ForteScarlet/Mock.java

廢話不多說,下面我會先展示一個例子,然後再來做對這個項目介紹。

- 0 創建項目

我是使用 IntelliJ IDEA 寫代碼的,但是不管用什麼,首先你需要創建一個maven項目,或者前去maven平臺下載 Mock.java 的jar包,以下我會使用 maven 項目進行舉例。

如果你選擇下載jar包需要注意其內部依賴了 commons-beanutils-1.9.3

導入座標:

<!-- https://mvnrepository.com/artifact/io.gitee.ForteScarlet/mock.java -->
<dependency>
    <groupId>io.gitee.ForteScarlet</groupId>
    <artifactId>mock.java</artifactId>
    <version>1.5.2</version>
</dependency>

然後等待依賴下載完成,創建一個有main方法的類準備進行測試。


- 0001 寫個User

畢竟是假數據,你需要一個載體。這裏我寫一個簡單的 User 類作爲測試用載體。記得重寫toString方法哦!方便測試。

public class User {
    private String name;
    private Integer age;
    
    private String email;
    private String password;
	
	// 以下省略掉 getter、 setter、 toString	
}

先寫一下看看效果。

    public static void main(String[] args) {
        System.out.println(new User());
    }

執行結果:

User{name='null', age=null, email='null', password='null'}

好,一切正常。那麼接下來,我想要:

  • name是一個隨機的中文名稱。
  • age 是一個18-80之間的隨機數。
  • email是一個隨機的163郵箱。
  • password是一個6-16位數的隨機字符。

那麼該怎麼辦呢?接着往下看吧~

- 0010 構建模板並使用

Mock.java中,生成一個“假對象”需要一個模板,然後通過解析這個模板中的參數以得知規則。
那麼根據我上面提到的規則,模板如下:

模板的載體是一個Map<String, Object>,且是可嵌套的。

 		// 準備模板載體
 		Map<String, Object> template = new HashMap<>(2);
        //name是一個隨機的中文名稱。
        template.put("name", "@cname");
        //age 是一個18-80之間的隨機數。
        template.put("age|18-80", 0);
        //email是一個隨機的163郵箱。
        template.put("email", "@email(163, com)");
        //password是一個6-16位數的隨機字符。
        template.put("password", "word(6,16)");

以上就是一個模板了。嗯?都是什麼意思?先彆着急,等我把這個例子講完
那麼模板怎麼用呢?使用類Mock的靜態方法即可:

1.x版本中,Mock類的全路徑:com.forte.util.Mock

		// 準備模板載體
        Map<String, Object> template = new HashMap<>();
        //name是一個隨機的中文名稱。
        template.put("name", "@cname");
        //age 是一個18-80之間的隨機數。
        template.put("age|18-80", 0);
        //email是一個隨機的163郵箱。
        template.put("email", "@email(163,com)");
        //password是一個6-16位數的隨機字符。
        template.put("password", "@word(6,16)");

        // 設置
        Mock.set(User.class, template);
        // 獲取一個MockObject
        MockObject<User> mockUser = Mock.get(User.class);
        // 拿到一個假對象
        User user = mockUser.getOne();
        // 打印展示
        System.out.println(user);

執行結果:

User{name='鮮于萍', age=38, email='[email protected]', password='gyqjkjo'}

我們可以看到,成功打印出來了有着數據的 User 對象。
那我要是想要很多個User怎麼辦呢?MockObject對象中提供了豐富的方法以供使用,下面我就舉幾個例子:

		// ...前置步驟省略
			
		// 獲取一個MockObject
        MockObject<User> mockUser = Mock.get(User.class);

 		// 拿到20個user的list
        List<User> list = mockUser.getList(20);
        // 拿到20個user的list,並轉化爲它們的toString字符串
        Set<String> set = mockUser.getSet(20, User::toString);
        // 獲取一個生成User對象的無限流
        Stream<User> stream = mockUser.getStream();
        // 通過並行流(即多線程)的方式獲取20個user對象
        List<User> listParallel = mockUser.getListParallel(20);
        // ... 還有很多

由此可見,MockObject提供了很多的方法用來對結果進行獲取、聚合,擁有很多 Java8Stream 流的特點,也支持各種 lambda 表達式,很是方便。
這時候又有問題來了,假如我沒有User對象,但是我還是想要假數據,並且想把這些字段作爲一個Map的鍵保存怎麼辦?很簡單,在上述示例的基礎上修改一下:

        // 準備模板載體
        Map<String, Object> template = new HashMap<>();
        //name是一個隨機的中文名稱。
        template.put("name", "@cname");
        //age 是一個18-80之間的隨機數。
        template.put("age|18-80", 0);
        //email是一個隨機的163郵箱。
        template.put("email", "@email(163,com)");
        //password是一個6-16位數的隨機字符。
        template.put("password", "@word(6,16)");

        // 設置
        Mock.set("user", template);
        // 獲取一個MockObject
        MockObject<Map> mockUser = Mock.get("user");
        Map userdata = mockUser.getOne();
        // 展示數據
        System.out.println(userdata);
        // 查看各個value指的class類型
        userdata.forEach((k, v) -> {
            System.out.println(k + "=" + v + "\t("+ v.getClass() +")");
        });

輸出結果如下:

{name=逯豐者, password=mropdfensmp, age=32, [email protected]}
name=逯豐者	(class java.lang.String)
password=mropdfensmp	(class java.lang.String)
age=32	(class java.lang.Integer)
[email protected]	(class java.lang.String)

我們可以看到,只要把 Mock.set(Class, Map) 換成 Mock.set(String, Map) 就好啦!這樣在用Mock.get的時候就用填入的字符串獲取,就會得到一個類型爲 MapMockObject,而這個Map中所存放的就是最終的值。而且這個 value 值的類型一般也是根據最終值決定的,比如上文中的age字段就是Integer類型。

- 0011 區間參數

詳細的介紹還請直接去 GITHUB 瞭解,GITHUB 的README文檔是中文的。

上面介紹完了基礎用法,但是大部分人看到這裏應該還是不明白上面那些@cnameage|18-80 的具體規則,儘管能夠猜到它們是什麼意思。
那接下來,我會簡單的介紹一下規則。

首先先看看作爲模板載體的 Map 中的 key 值。key 值稱爲(自稱) 字段映射,顧名思義就是用來定義其所指向的字段的。一個字段映射的格式如下:
field_name[|p1[-p2][.p3[,p4]]]
看上去有點亂哈,簡單來說,中括號[]中標註的內容爲可選參數,同時被稱爲(自稱) 區間參數
其中p1p2爲整數位參數,p3p4爲小數位參數。
如果有參數,則參數與字段之間用豎線|分割,同位數參數用短橫線-分割(也就是減號),不同位數參數用點.分割,且小數位在後,整數位在前。
舉幾個例子吧:
name - 無參數
age|18 - 只有一個整數位參數
age|18-80 - 有兩個整數位參數
money|100-200.0-2 - 有兩個整數位參數和兩個小數位參數
money|100-200.2 - 有兩個整數位參數與一個小數位參數

- 0100 @函數

詳細的介紹還請直接去 GITHUB 瞭解,GITHUB 的README文檔是中文的。

對,沒錯,就叫“艾特函數”。顧名思義,就是開頭帶着一個@的函數。@函數一般會用在Map模板的value值中,例如:

		 // 準備模板載體
        Map<String, Object> template = new HashMap<>();
        //name是一個隨機的中文名稱。
        template.put("name", "@cname");

上面這個@cname就是一個@函數,代表獲取一個隨機的中文姓名並賦值給name字段。
那麼這種@函數一共都有那些呢?根據原作者的話說,所有的@函數都是在初始化的時候動態讀取MockUtil這個類的靜態方法而得到的,所以如果想知道有哪些@函數可以用,直接進入到MockUtil這個類中看看就可以了。
值得一提的是,@函數可以存在參數,也可以存在多個。當有多個可解析的@函數的時候,其最終結果會被嘗試使用+運算進行計算。不過目前還不支持@函數的嵌套

// 準備模板載體
        Map<String, Object> template = new HashMap<>();
        //email是一個隨機的163郵箱。
        template.put("email", "@email(163,com)");
        // name = 中文名 + 2-20之間的隨機值
        template.put("name", "@cname@integer(2,20)");

上面所得到的結果示例如下:

[email protected]
name=茅吾表10

不過需要注意的是,@函數的多個參數之間直接用逗號,分隔且不能存在空格。

- 0101 翻翻源代碼

大概的使用方式已經知道了,那麼它的實現原理到底是什麼呢?不過其實不用說大家也大概都能猜到了,就是通過反射獲取字段,然後解析定義的那些規則,再進行封裝,最終通過反射獲取你想要的JavaBean,並對它們賦值,然後提供給使用者。

其內部定義了不少針對不同的value參數的解析器用來應對各種各樣的情況,而作者也很“貼心”的在github中爲整個框架中一些比較核心的類或者接口取了個名字並做了簡單的介紹。獲取沒有用到什麼比較高深的東西,甚至有可能會有一些比較低級的代碼,但是它依舊可以作爲一個入門級的小項目用來研究與學習。

總而言之就是反射,沒有什麼大道理。

- 0110 有點眼熟?

有些人可能在看到這個age|1-2.0-1的時候就覺得有點眼熟:這不是前端的假數據測試庫mock.js的語法麼?然後再看看這個項目名:Mock.java,嗯?有一種恍然大悟的感覺。
沒錯,作者的靈感就是來源於Mock.js,並且這種區間函數@函數的語法格式也是借鑑了Mock.js來的,這些在他github項目的簡介處和文檔處也是有做說明的。

- 0110 結尾

大概就是這樣,只是做一些簡單的介紹,而一些具體的規則、邏輯,可能還是要去看看官方的github文檔或者自己手動嘗試一下。同時希望在看github的時候,能夠作者點亮一顆star,以示鼓勵 😃
嗯?你問我爲什麼要幫作者討要star?因爲我就是作者丫~ 😄

新手寫文章,如有不妥請諒解~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章