如何采用Java構(gòu)建逼真的測試數(shù)據(jù):開發(fā)人員的實踐指南
譯文
譯者 | 李睿
審校 | 重樓
在日常開發(fā)工作中,無論是后端工程師還是API開發(fā)人員,幾乎都會面臨一個共同的需求:如何獲取高質(zhì)量的模擬數(shù)據(jù)。
無論是在測試新的API、為了演示填充數(shù)據(jù)庫,還是讓單元測試用例更加貼近真實場景,都需要借助模擬數(shù)據(jù)來完成。但問題在于,很多常見的模擬數(shù)據(jù)顯得過于“生硬”和“重復(fù)”——例如反復(fù)出現(xiàn)的“John Doe”和“123 Main Street”。這不僅降低了測試的真實性,也在向團隊或客戶演示時影響了整體體驗。
本文將探索DataFaker和EasyRandom這兩個功能強大的Java庫,它們可以輕而易舉地生成大量且逼真的模擬數(shù)據(jù)。
這并不只是簡單地生成姓名與郵箱,還將演示如何在Spring Boot 3項目中無縫集成它們,并結(jié)合二者優(yōu)勢以發(fā)揮最大作用。最終,你將學(xué)會如何構(gòu)建一個可直接返回模擬數(shù)據(jù)的 REST API。
這并不是紙上談兵,而是配備了真實可用的代碼示例,確保能夠在項目中直接應(yīng)用。
為什么要生成模擬數(shù)據(jù)?
開發(fā)人員通常面對這樣的現(xiàn)實:人工制作測試數(shù)據(jù)既耗時又容易出錯。
假設(shè)開發(fā)人員正在開發(fā)一個用戶管理系統(tǒng)。需要測試分頁、過濾、排序和邊緣情況(例如缺失電子郵件或者非常長的姓名)。與其人工編寫100行示例JSON,不如立即自動并即時生成,這不是更好嗎?
優(yōu)質(zhì)的模擬數(shù)據(jù)可以幫助開發(fā)人員:
- 在更真實的場景中驗證其邏輯。
 - 使用“看起來真實”的數(shù)據(jù)展示原型。
 - 使用可變輸入對API或UI組件進行壓力測試。
 - 自動化單元測試,無需樣板“模擬構(gòu)建器”。
 
因此,開發(fā)人員不再輸入人工模擬數(shù)據(jù),而是讓DataFaker和EasyRandom來完成繁重的工作。
DataFaker:現(xiàn)代化以及改進版的JavaFaker
對于曾經(jīng)使用過JavaFaker的開發(fā)人員來說,DataFaker 是其現(xiàn)代化且得到積極維護的后繼產(chǎn)品。
它專為現(xiàn)代Java 生態(tài)(Java 17+)構(gòu)建,不僅性能優(yōu)異,更提供了涵蓋姓名、地址、財務(wù)信息、公司數(shù)據(jù)、互聯(lián)網(wǎng)數(shù)據(jù)以及加密密鑰等數(shù)百種數(shù)據(jù)類別,能夠滿足多樣化的測試數(shù)據(jù)需求。
以下是一個簡單的示例:
Java
import net.datafaker.Faker;
Faker faker = new Faker();
System.out.println(faker.name().fullName());
System.out.println(faker.internet().emailAddress());
System.out.println(faker.address().fullAddress());在運行之后,將會得到類似這樣的結(jié)果:
Plain Text
Matilde Marques
matilde.marques@techmail.com
Rua do Carmo 45, 1200-093 Lisboa此外,通過其區(qū)域設(shè)置功能,生成的數(shù)據(jù)還能根據(jù)不同的地區(qū)動態(tài)調(diào)整,實現(xiàn)內(nèi)容的本地化。
Java
Faker faker = new Faker(new Locale("pt"));現(xiàn)在,生成的數(shù)據(jù)已經(jīng)可以匹配開發(fā)人員所使用的語言和所在地區(qū)——這無疑為國際化測試帶來了極大便利。
EasyRandom:超越字段填充的智能對象生成器
與專注于生成逼真字段數(shù)據(jù)的DataFaker不同,EasyRandom(前身為Random Beans)提供了另一種思路。
當面對包含復(fù)雜結(jié)構(gòu)(如實體類或DTO)的Java對象,并希望自動填充隨機但符合規(guī)范的數(shù)據(jù)時,EasyRandom顯得尤為實用。
可以將其視為一個智能的“對象構(gòu)建引擎”:它不僅能填充基本類型字段,還能自動處理嵌套對象、集合(如List、Map)等復(fù)雜結(jié)構(gòu),輕松生成完整且結(jié)構(gòu)正確的對象實例。
示例:
Java
import org.jeasy.random.EasyRandom;
EasyRandom easyRandom = new EasyRandom();
Person randomPerson = easyRandom.nextObject(Person.class);這將創(chuàng)建一個完全填充的Person實例,其中包含隨機字符串、數(shù)字,甚至嵌套屬性。
因此,DataFaker提供了真實感(例如,“John Smith, john@smith.com”),EasyRandom提供了對象結(jié)構(gòu)的自動化構(gòu)建能力(例如填充整個POJO圖)。
最佳實踐是將二者結(jié)合使用:讓EasyRandom創(chuàng)建對象,然后使用DataFake更加可信的數(shù)據(jù)來修飾特定的字段。
DataFaker與EasyRandom的結(jié)合:最佳選擇
這就是事情變得有趣的地方。
為此將創(chuàng)建一個小型Spring Boot REST API,它公開用于生成虛擬用戶的端點。每個用戶將有一個id、fullName、email、phone和address。你將使用DataFaker實現(xiàn)真實感,使用EasyRandom實現(xiàn)自動化。項目結(jié)構(gòu)如下:
Plain Text
src/
 ├─ main/java/com/example/fakedata/
 │   ├─ Application.java
 │   ├─ config/
 │   ├─ api/
 │   ├─ controller/
 │   ├─ domain/
 │   ├─ dto/
 │   ├─ service/
 │   └─ mapper/
 └─ resources/
     └─ static/index.html用戶域分類
為了保持簡潔的原則,使用Lombok來避免樣板代碼:
Java
 @Data
 @Builder
 public class User {
  private String id;
  private String fullName;
  private String email;
  private String phone;
  private String address;
 }對于API響應(yīng),將使用Java記錄(Java record)實現(xiàn)不可變性和可讀性:
Java
public record UserDto(String id, String fullName, String email, String phone, String address) { }服務(wù):兩個庫相結(jié)合
這是項目的核心:
Java
 @Service
 public class DataGenService {
  private final Faker faker = new Faker(Locale.ENGLISH);
  private final EasyRandom easyRandom;
  public DataGenService() {
    EasyRandomParameters params = new EasyRandomParameters()
        .seed(System.currentTimeMillis())
        .stringLengthRange(5, 20);
    this.easyRandom = new EasyRandom(params);
  }
  public User randomUserViaDatafaker() {
    return User.builder()
        .id(UUID.randomUUID().toString())
        .fullName(faker.name().fullName())
        .email(faker.internet().emailAddress())
        .phone(faker.phoneNumber().cellPhone())
2       .address(faker.address().fullAddress())
       .build();
  }
  public User randomUserViaEasyRandom() {
    User u = easyRandom.nextObject(User.class);
    if (u.getId() == null || u.getId().isBlank()) {
      u.setId(UUID.randomUUID().toString());
    }
    u.setFullName(faker.name().fullName());
    u.setEmail(faker.internet().emailAddress());
    return u;
  }
  public List<User> manyUsers(int count, boolean easyRandomMode) {
    return IntStream.range(0, count)
        .mapToObj(i -> easyRandomMode ? randomUserViaEasyRandom() : randomUserViaDatafaker())
        .collect(Collectors.toList());
  }
 }可以看到上例如何使用DataFaker實現(xiàn)真實感以及使用EasyRandom構(gòu)建結(jié)構(gòu)。二者的協(xié)作,就像默契配合的兩位廚師:一位專注構(gòu)建菜肴的整體架構(gòu)與食材搭配,另一位則精心調(diào)配風味與細節(jié)。最終,它們共同呈現(xiàn)出的是結(jié)構(gòu)完整、細節(jié)逼真的高質(zhì)量數(shù)據(jù)。
REST控制器
現(xiàn)在,通過REST API來使其具備可訪問性。
Java
 @RestController
 @RequestMapping("/api/users")
 public class UserController {
  private final DataGenService service;
  public UserController(DataGenService service) {
    this.service = service;
  }
  @GetMapping("/{count}")
  public ApiResponse<List<UserDto>> generateUsers(@PathVariable int count,
                                           @RequestParam(defaultValue = "false") boolean easy) {
    List<UserDto> users = service.manyUsers(count, easy)
                                 .stream().map(UserMapper::toDto)
                                 .collect(Collectors.toList());
    return ApiResponse.of(users);
  }
 }為了使API響應(yīng)一致,將所有內(nèi)容包裝在一個帶有時間戳的信封中:
Java
 public record ApiResponse<T>(T data, Instant timestamp) {
  public static <T> ApiResponse<T> of(T data) {
    return new ApiResponse<>(data, Instant.now());
  }
 }這樣,每個API調(diào)用都會返回這樣的數(shù)據(jù):
JSON
 {
  "data": [
    {
      "id": "e7b1c37a-8b20-43c1-8ff3-b4aef8d89c3a",
      "fullName": "Lina Cordeiro",
      "email": "lina.cordeiro@example.com",
      "phone": "+351 912 345 678",
      "address": "Rua do Comércio 12, Porto"
    }
  ],
  "timestamp": "2025-10-06T13:02:45.321Z"
 }這樣處理更加清晰且易于調(diào)試。
為什么在響應(yīng)中添加時間戳?
當在分布式系統(tǒng)中調(diào)試請求或當客戶端記錄響應(yīng)時,在有效負載中直接包含服務(wù)器時間戳有助于關(guān)聯(lián)事件——這是一個具有宏觀效益的微觀細節(jié)。
為什么兩個庫結(jié)合使用更好?
有人會問,“為什么不單獨使用DataFaker?”這是一個很好的問題:
- DataFaker在生成逼真數(shù)據(jù)方面表現(xiàn)出色,但它不擅長自動填充深層次的對象結(jié)構(gòu)。
 - 另一方面,EasyRandom擅長處理復(fù)雜的對象圖,但其隨機性往往顯得“過于合成”——例如像“asdlkfj@example.com”這樣缺乏真實感的示例。
 
將兩者結(jié)合,能夠獲得以下優(yōu)勢:
- 真實感和自動化的統(tǒng)一
 - 與測試框架及API的無縫集成
 - 通過配置與隨機種子確保數(shù)據(jù)的一致性
 
這有點像將隨機單詞生成器與翻譯器結(jié)合起來:一個提供了豐富多樣的詞匯,而另一個則將這些詞匯組織成結(jié)構(gòu)完整、意義明確的篇章。
更進一步:Postman、Docker和CI/CD
完整的項目還包括:
- 用于快速測試的Postman集合
 - 用于容器化的Dockerfile和docker-compose.yml
 - 用于自動化構(gòu)建和依賴項更新的GitHub Actions CI和Dependabot設(shè)置
 
這使得這個小演示成為一個用于測試和學(xué)習的生產(chǎn)級參考項目。
如果正在指導(dǎo)初級開發(fā)人員或構(gòu)建內(nèi)部工具,這是一個展示清晰架構(gòu)和可重復(fù)數(shù)據(jù)生成的推薦示例。
代碼庫:github.com/wallaceespindola/fake-data-springboot
使用這一設(shè)置的實際想法
- 負載測試:生成數(shù)千個模擬用戶來填充數(shù)據(jù)庫。
 - UI原型設(shè)計:用逼真的API數(shù)據(jù)為前端提供數(shù)據(jù)。
 - 演示環(huán)境:采用動態(tài)樣本用戶填充沙盒環(huán)境。
 - 單元測試:用對DataGenService.randomUserViaDatafaker()的調(diào)用替換new User("a","b")。
 - 數(shù)據(jù)匿名化:快速采用模擬數(shù)據(jù)替換敏感的生產(chǎn)數(shù)據(jù)。
 
這些都是這種組合發(fā)揮重要作用的真實場景。
結(jié)束語
讓測試數(shù)據(jù)從令人乏味的模擬數(shù)據(jù),轉(zhuǎn)變?yōu)橐巳雱俚摹罢鎸崱毖菔?,其秘訣就在于所選擇的工具。
使用DataFaker和EasyRandom,可以自動化實現(xiàn)該過程——使用現(xiàn)代Java、更少的代碼以及完美組合的庫。不僅可以在構(gòu)建測試或模擬API時節(jié)省時間,還可以提供生動、多樣和逼真的演示。
這一切都構(gòu)建于開源、輕量的基礎(chǔ)之上,并能輕松融入開發(fā)人員熟悉的任何技術(shù)堆?!獰o論是Spring Boot、Quarkus、Micronaut,還是簡單的控制臺應(yīng)用程序。
現(xiàn)在是告別模擬數(shù)據(jù)的時候了,為開發(fā)的項目提供個性與真實感的數(shù)據(jù),可以讓Java完成這些繁重的工作。
需要了解更多的技術(shù)見解?可以查看GitHub代碼庫和LinkedIn頁面。
原文標題:Building Realistic Test Data in Java: A Hands-On Guide for Developers,作者:Wallace Espindola















 
 
 











 
 
 
 