【測試】 Java如何優雅的生成測試數據

image.png

前言


在日常的測試中,我們經常需要提前準備一大堆測試數據,用來驗證業務邏輯。當然對於簡單的數據類型完全可以通過 JDK 自帶的 Random 類來實現。但是對於一個比較複雜的類,或者參數的格式有特殊要求的時候,Random 就不適用了,這個時候就需要藉助一些能夠生成測試數據的框架。

相關框架


我在實際調研中,找到了 2 個在個人看來還不錯的生成框架,他們分別是:

  1. jmockdata
  2. java-faker


下面我將一一介紹這些框架的優缺點以及適用場景。話不多說,直接開始擼代碼。

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 protected],idCard=V5bBdX,name=KM8]


可以看到 ageemail 已經正常了,可以通過他強大的配置功能對於數據進行生成的限制,但是你也發現了,對於一些有簡單邊界的數據,這樣做可以,否則就像 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;


步驟:

  1. 創建 faker 對象
  2. 通過 faker 對象獲得要生成的實體對象
  3. 調用實體對象獲得對於生成的部分


這裏的實體對象,對應上面的 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: 簡單易用的隨機數據生成器。這個項目對於本地化數據,做了很多處理,基本夠用。

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