前言
在日常的測試中,我們經常需要提前准備一大堆測試數據,用來驗證業務邏輯。當然對於簡單的數據類型完全可以通過 JDK 自帶的 Random
類來實現。但是對於一個比較復雜的類,或者參數的格式有特殊要求的時候,Random
就不適用了,這個時候就需要借助一些能夠生成測試數據的框架。
相關框架
我在實際調研中,找到了 2 個在個人看來還不錯的生成框架,他們分別是:
下面我將一一介紹這些框架的優缺點以及適用場景。話不多說,直接開始擼代碼。
JmockData
首先出場的是 JmockData 框架,它是官方定義如下:
一款實現模擬JAVA類型或對象的實例化並隨機初始化對象的數據的工具框架。
依賴
<dependency>
<groupId>com.github.jsonzou</groupId>
<artifactId>jmockdata</artifactId>
<version>4.2.0</version>
</dependency>
基礎類型數據生成
@Test
public void testBaseType(){
// 基礎數據類型
System.out.println(JMockData.mock(byte.class));
System.out.println(JMockData.mock(int.class));
System.out.println(JMockData.mock(long.class));
System.out.println(JMockData.mock(double.class));
System.out.println(JMockData.mock(float.class));
System.out.println(JMockData.mock(String.class));
System.out.println(JMockData.mock(BigDecimal.class));
// 基礎數據類型的數組
System.out.println(JMockData.mock(byte[].class));
System.out.println(JMockData.mock(int[].class));
System.out.println(JMockData.mock(long[].class));
System.out.println(JMockData.mock(double[].class));
System.out.println(JMockData.mock(float[].class));
System.out.println(JMockData.mock(String[].class));
System.out.println(JMockData.mock(BigDecimal[].class));
}
運行結果
0
2610
3401
8582.18
7194.44
5Xu7
9051.92
[B@7fbe847c
[I@41975e01
[J@c2e1f26
[D@dcf3e99
[F@6d9c638
[Ljava.lang.String;@7dc5e7b4
[Ljava.math.BigDecimal;@1ee0005
JavaBean 類型數據生成
/**
* java bean 測試
*/
@Test
public void testJavaBean(){
Person mock = JMockData.mock(Person.class);
System.out.println(mock);
}
運行結果
Person[address=RrayfQIK,age=5863,idCard=SDn,name=j]
這里可以看到,使用 JMockdata.mock(xx.class);
可以很容易的生成一個 JavaBean。框架通過反射,在底層遍歷獲得類的屬性與類型,然后填充數據。
但是與此同時,大家也發現了,雖然我們可以的的確確的生成了一個 Person
類,也給它的每個屬性都填充了值,但是生成的數據只是根據類型簡單生成的,比如 age
字段被填充的是 5863。如果數據有現實含義,沒有規則的隨機就很容易出現烏龍。
要解決這個問題,我們就要限制隨機數據的范圍,可以通過它的配置功能實現。
使用隨機配置
@Test
public void testJavaBeanWithConfig() {
MockConfig mockConfig =
new MockConfig()
.subConfig("age")
// 設置 int 的范圍
.intRange(1, 100)
.subConfig("email")
// 設置生成郵箱正則
.stringRegex("[a-z0-9]{5,15}\\@\\w{3,5}\\.[a-z]{2,3}")
.globalConfig();
Person mock = JMockData.mock(Person.class, mockConfig);
System.out.println(mock);
}
運行結果
Person[address=hXttj2s,age=2,email=w14hnn@UvFB9.kt,idCard=V5bBdX,name=KM8]
可以看到 age
跟 email
已經正常了,可以通過他強大的配置功能對於數據進行生成的限制,但是你也發現了,對於一些有簡單邊界的數據,這樣做可以,否則就像 address 、 name 這樣的數據,很難通過簡單規則去生成。
而對於有現實意義的數據生成,可以使用 java-faker 框架。
Java-faker
依賴
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
數據生成
@Test
public void testRandomName() {
Faker faker = new Faker();
final Name name = faker.name();
System.out.println("firstName : " + name.firstName());
System.out.println("username : " + name.username());
System.out.println("bloodGroup : " + name.bloodGroup());
System.out.println("suffix : " + name.suffix());
System.out.println("title : " + name.title());
System.out.println("lastName : " + name.lastName());
System.out.println("nameWithMiddle : " + name.nameWithMiddle());
System.out.println("fullName : " + name.fullName());
System.out.println("name : " + name.name());
System.out.println("prefix : " + name.prefix());
}
生成結果
firstName : Hollis
username : cristy.white
bloodGroup : O-
suffix : Sr.
title : Product Implementation Specialist
lastName : Johnston
nameWithMiddle : Alesia Hagenes Kiehn
fullName : Dr. Pat Marvin
name : Ms. Jamal Rau
prefix : Mr.
可以看到 java-faker 生成數據特別的方便,基本格式如下:
Faker faker = new Faker();
final Xx xx = faker.xx();
xx.yyyy;
步驟:
- 創建 faker 對象
- 通過 faker 對象獲得要生成的實體對象
- 調用實體對象獲得對於生成的部分
這里的實體對象,對應上面的 name,也就說我們要生成姓名相關的數據,拿到實體對象后還可以只獲得其中的部分數據,比如姓名中的姓或名,還有前綴,甚至血型,可以說是非常全面。
而且 java-faker 支持的實體對象特別的多,如下:
- Address
- Ancient
- Animal
- App
- Aqua Teen Hunger Force
- Artist
- Avatar
- Back To The Future
- Aviation
- Basketball
- Beer
- Bojack Horseman
- Book
- Bool
- Business
- ChuckNorris
- Cat
- Code
- Coin
- Color
- Commerce
- Company
- Crypto
- DateAndTime
- Demographic
- Disease
- Dog
- DragonBall
- Dune
- Educator
- Esports
- File
- Finance
- Food
- Friends
- FunnyName
- GameOfThrones
- Gender
- Hacker
- HarryPotter
- Hipster
- HitchhikersGuideToTheGalaxy
- Hobbit
- HowIMetYourMother
- IdNumber
- Internet
- Job
- Kaamelott
- LeagueOfLegends
- Lebowski
- LordOfTheRings
- Lorem
- Matz
- Music
- Name
- Nation
- Number
- Options
- Overwatch
- PhoneNumber
- Pokemon
- Princess Bride
- Relationship Terms
- RickAndMorty
- Robin
- RockBand
- Shakespeare
- SlackEmoji
- Space
- StarTrek
- Stock
- Superhero
- Team
- TwinPeaks
- University
- Weather
- Witcher
- Yoda
- Zelda
從身份證到姓名再到地址、動物、書籍、頭像、職位等等,基本上覆蓋了我們生活中的方方頁面。
另外,java-faker 更貼心的是幫我們實現了國際化,可能剛才看了姓名的例子,有些朋友覺得這個框架好看但不好用,就拿生成姓名來說,生成都是 Johnston、Tom、Kiwi 之類英文名,在國內很少用到這些數據。其實java-faker 已經考慮到這個問題。而且只要改一行代碼就可以了。
修改后的代碼
// 原代碼 Faker faker = new Faker();
// 新代碼
Faker faker = new Faker(Locale.CHINA);
final Name name = faker.name();
System.out.println("firstName : " + name.firstName());
System.out.println("username : " + name.username());
System.out.println("bloodGroup : " + name.bloodGroup());
System.out.println("suffix : " + name.suffix());
System.out.println("title : " + name.title());
System.out.println("lastName : " + name.lastName());
System.out.println("nameWithMiddle : " + name.nameWithMiddle());
System.out.println("fullName : " + name.fullName());
System.out.println("name : " + name.name());
System.out.println("prefix : " + name.prefix());
生成結果
firstName : 熠彤
username : 燁霖.龍
bloodGroup : A-
suffix : IV
title : Investor Division Engineer
lastName : 范
nameWithMiddle : 胡思
fullName : 孟鴻濤
name : 黎航
prefix : Miss
只需要,把之前的 Faker faker = new Faker(); 改成 Faker faker = new Faker(Locale.CHINA); 即可。如果你想生成其它國家的內容也是可以的,java-faker 支持的國家如下:
- bg
- ca
- ca-CAT
- da-DK
- de
- de-AT
- de-CH
- en
- en-AU
- en-au-ocker
- en-BORK
- en-CA
- en-GB
- en-IND
- en-MS
- en-NEP
- en-NG
- en-NZ
- en-PAK
- en-SG
- en-UG
- en-US
- en-ZA
- es
- es-MX
- fa
- fi-FI
- fr
- he
- hu
- in-ID
- it
- ja
- ko
- nb-NO
- nl
- pl
- pt
- pt-BR
- ru
- sk
- sv
- sv-SE
- tr
- uk
- vi
- zh-CN
- zh-TW
總結
JmockData
個人感覺它是 plus 版的 Random 類,方便簡單的按類型生成數據,也可以自己給定配置與規則去生成,缺點,上文也說了,生成的數據沒有太多實際意義,簡單數據還好,如果像姓名、地址等有現實意義的數據,就不太合適了。
Java-faker
java-faker 其實是遷移自 ruby 中大名鼎鼎的 faker。很多語言都有他的對應遷移,比如 python、java。所以數據量和功能是很完善並且經過考驗的,使用起來也很方便。實際工作中,可以優化使用。如果要說缺點,個人覺得他有些地方國際化的並不全面,比如車牌、身份證之類的。如果對於這些數據有比較嚴格的要求,推薦另一個項目 yindz/common-random: 簡單易用的隨機數據生成器。這個項目對於本地化數據,做了很多處理,基本夠用。