開源神器:自動生成隨機 mock 資料測試物件

老馬嘯西風發表於2020-12-25

測試的痛點

大家好,我是老馬。

每一位開發者大部分工作都是寫程式碼、測試程式碼、修BUG。

測試BUG.jpg

我們有很多測試程式碼,總是花費大量的實踐去構建一個物件。

於是就在想,能不能自動填充一個物件呢?

於是去 github 查了一下,找到了一個測試神器 data-factory。

https://github.com/houbb/data-factory/

data-factory

作用

data-factory 專案用於根據物件,隨機自動生成初始化資訊。便於測試。

特性

  • 8 大基本型別的支援

  • 陣列、物件、列舉、Map、連結串列、Set 等支援

  • String、BigDecimal、BigInteger、Currency 等常見型別的支援

  • Date、LocalDate、LocalDateTime、LocalTime、Year 等常見日期型別支援

  • 支援 Regex 正規表示式

  • @DataFactory 註解支援靈活配置

快速入門

引入依賴

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>data-factory-core</artifactId>
    <version>0.0.8</version>
</dependency>

我們通過 DataUtil.build(class) 就可以生成對應類的隨機值。

比如 DataUtil.build(String.class);,就可以生成隨機的字串:

0s5Z8foS1

老馬發現,基本支援所有常見的型別,我們指定對應的 class 即可,這點還是挺方便的。

不過我一般都是使用物件,那可以自動填充一個物件嗎?

自動化.jpg

物件 bean 填充

當然,最常用的還是初始化一個 java 物件。

public class User {

    private String name;

    private int age;

    private Date birthday;

    private List<String> stringList;

    //S/F 的列舉
    private StatusEnum statusEnum;

    private Map<String, String> map;
    
    //Getter & Setter
}

構建方法 User user = DataUtil.build(User.class);

構建物件如下:

User{name='wZ8CJZtK', age=-564106861, birthday=Wed Feb 27 22:14:34 CST 2019, stringList=[Du4iJkQj], statusEnum=S, map={yA5yDqM=Kdzi}}

內容每次都隨機,便於基本的測試資料填充。

@DataFactory 註解

當然,有時候我們希望生成的資料符合一定的規則,這個時候可以通過 @DataFactory 註解去進行限制。

註解屬性

/**
 * 資料生成註解
 * @author binbin.hou
 * @date 2019/3/9
 * @since 0.0.2
 */
@Inherited
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataFactory {

    /**
     * 是否忽略此欄位
     *
     * @return 預設不忽略
     */
    boolean ignore() default false;

    /**
     * 數字整數部分最大值。
     * 只作用於數字型別的欄位
     *
     * @return 返回最大值
     */
    int max() default 100;

    /**
     * 數字整數部分最小值。
     * 只作用於數字型別的欄位
     *
     * @return 返回最小值
     */
    int min() default 0;

    /**
     * 精度。
     * 作用於Float、Double、BigDecimal 小數部分長度
     *
     * @return 返回精度
     */
    int precision() default 2;

    /**
     * 最大長度。只作用於String型別的欄位
     *
     * @return 返回最大長度
     */
    int maxLen() default 30;

    /**
     * 最小長度。只作用於String型別的欄位
     *
     * @return 返回最小長度
     */
    int minLen() default 1;

    /**
     * 指定當前欄位的類實現策略
     * @return 實現類
     * @since 0.0.6
     */
    Class<? extends IData> data() default IData.class;

    /**
     * 正規表示式
     * 1. 當前版本為了簡單方便,如果 regex 存在,則直接忽略長度,精度等其他註解配置。
     * 2. 建議直接使用在 String 型別
     * 3. 如果使用其他型別,則必須保證提供了對應的 String 構造器。如{@link Long#Long(String)}
     * 4. 基本型別會直接使用對應的包裝型別。
     * @since 0.0.3
     * @return 表示式資訊
     */
    String regex() default "";

}

String 類

  • 定義物件
/**
 * 字串類註解測試
 * @author binbin.hou
 * @date 2019/3/9
 * @since 0.0.2
 */
public class UserAnnotationString {

    /**
     * 指定最小長度,最大長度
     */
    @DataFactory(minLen = 2, maxLen = 10)
    private String name;

    /**
     * 忽略生成當前欄位
     */
    @DataFactory(ignore = true)
    private String hobby;

    //Getter & Setter

}
  • 測試程式碼
/**
*
* Method: build(clazz)
*/
@Test
public void stringAnnotationTest() throws Exception {
    for(int i = 0; i < 100; i++) {
        UserAnnotationString userAnnotationString = DataUtil.build(UserAnnotationString.class);

        Assertions.assertNull(userAnnotationString.getHobby());
        Assertions.assertTrue(userAnnotationString.getName().length() >= 2);
        Assertions.assertTrue(userAnnotationString.getName().length() <= 10);
    }
}

Number 類

  • 物件定義
/**
 * 數字類註解測試
 * @author binbin.hou
 * @date 2019/3/9
 * @since 0.0.2
 */
public class UserAnnotationNumber {

    @DataFactory(min = 10, max = 20)
    private Byte aByte;

    @DataFactory(min = 10, max = 20)
    private Short aShort;

    @DataFactory(min = 10, max = 20)
    private Integer integer;

    @DataFactory(min = 10, max = 20)
    private Long aLong;

    @DataFactory(min = 10, max = 20, precision = 3)
    private Double aDouble;

    @DataFactory(min = 10, max = 20, precision = 3)
    private Float aFloat;

    @DataFactory(min = 10, max = 20, precision = 3)
    private BigDecimal bigDecimal;

    @DataFactory(min = 10, max = 20)
    private BigInteger bigInteger;

    //Getter & Setter

}
  • 測試程式碼

通過 DataUtil.build(UserAnnotationNumber.class) 生成的物件如下:

UserAnnotationNumber{aByte=10, aShort=17, integer=19, aLong=11, aDouble=19.888, aFloat=10.067, bigDecimal=18.035, bigInteger=13}

正規表示式

正規表示式作為一大神器,自然是不能落下。

定義

物件的定義如下:

/**
 * 正規表示式測試物件
 * @author binbin.hou
 * @date 2019/3/12
 * @since 0.0.3
 */
public class RegexBean {

    @DataFactory(regex = "[0-3]([a-c]|[e-g]{1,2})")
    private String name;

    @DataFactory(regex = "[0-9]{1,2}")
    private int age;

    @DataFactory(regex = "[0-9]{1,2}")
    private BigDecimal amount;

    //Getter & Setter
    
}

效果

生成效果如下:

RegexBean{name='2c', age=61, amount=39}

自定義 Data 生成策略

當然,所有的內建策略只能滿足最常見的需求。

但是無法滿足各種特殊的定製化策略,幸運的是我們可以自定義自己的資料填充策略。

自定義生成策略

這裡我們實現一個最簡單的生成策略,如果是字串,固定為 123。

public class MyStringData implements IData<String>  {

    @Override
    public String build(IContext context, Class<String> stringClass) {
        return "123";
    }

}

使用

我們在 @DataFactory 註解中指定自己的策略。

public class UserAnnotationData {

    @DataFactory(data = MyStringData.class)
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

這樣生成的就是我們自己的資料生成策略。

不足之處

當然,老馬覺得這些特性還是不太方便。

希望作者可以實現支援全域性配置之類的特性,這樣會更加方便的。

各位小夥伴也可以體驗一下,讓自己早點下班,享受屬於自己的時光。

下班.jpg

小結

今天我們和大家一起感受了資料填充工具的便利性,大家工作中有需要就可以用起來。

為了便於大家學習,所有原始碼均已開源:

物件填充:https://github.com/houbb/data-factory

效能壓測:https://github.com/houbb/junitperf

希望本文對你有所幫助,如果喜歡,歡迎點贊收藏轉發一波。

我是老馬,期待與你的下次相遇。
在這裡插入圖片描述

相關文章